Flutterアプリケーション開発概論

【owner登録】チーム作成者を自動でownerとして保存する

20LINE風チームタスク管理アプリを作りながら、ログイン・データベース・権限管理を学ぶ
FlutteriOSAndroidMacOSWindows基礎から学ぶ開発アプリ開発

このページでやること

このページでは、チームを作った人を、自動で owner として保存する処理を確認します。

owner とは、そのチームの所有者です。

チームを作った人は、そのチームを管理できる人にする必要があります。

そのため、チーム作成時に、次の場所へ自動で保存します。

teams/{teamId}/members/{uid}

保存する内容は、次のようになります。

uid: ログイン中ユーザーのuid
email: ログイン中ユーザーのメール
displayName: ログイン中ユーザーの名前
role: owner
joinedAt: 参加日時

今日のゴール

チームを作った人が、自動で owner として登録されるようにします。

ログイン中のユーザーがチームを作る
↓
teams/{teamId} を作る
↓
teams/{teamId}/members/{uid} を作る
↓
role に owner を保存する

この role: owner がとても大事です。


このページで出てくる単語

単語一言説明
ownerチームの所有者
roleその人の権限
uidユーザーごとのID
membersチームに参加している人の一覧
doc(user.uid)ユーザーIDを使って保存場所を決める
set()指定した場所にデータを保存する
joinedAtチームに参加した日時

権限とは、その人が何をできるかを決めるルールです。


npmや環境変数はこのページで必要?

このページでは、npmは使いません。

環境変数も設定しません。

必要なのは、すでに使っている次の2つです。

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

このページでは、Firestoreへ owner を保存する部分だけを確認します。


Step 1:main.dartを開く

次のファイルを開きます。

lib/main.dart

ターミナルから開く場合は、次を実行します。

code lib/main.dart

code が使えない場合は、VS Codeの左側から lib/main.dart を開いてください。


Step 2:createTeam()を探す

main.dart の中で、次の関数を探します。

createTeam

VS Codeでは、次で検索できます。

command + F

検索する文字はこれです。

Future<void> createTeam

この createTeam() が、チームを作る処理です。


Step 3:ログイン中ユーザーを取得する

createTeam() の中に、次のコードがあるか確認します。

final user = FirebaseAuth.instance.currentUser;

currentUser は、今ログインしているユーザーです。

この user から、uid やメールアドレスを取得します。

user.uid
user.email

uid は、ユーザーごとにFirebaseが作るIDです。


Step 4:ログインしていない場合を止める

次のコードも確認します。

if (user == null) {
  setSheetState(() {
    errorText = 'ログイン状態を確認できません。';
  });
  return;
}

user == null は、ログイン中のユーザーがいないという意味です。

ログインしていない人はチームを作れないので、ここで処理を止めます。

return は、ここで処理を終了するという意味です。


Step 5:ユーザーのプロフィールを取得する

次に、users/{uid} からプロフィールを取得します。

final userDoc = await FirebaseFirestore.instance
    .collection('users')
    .doc(user.uid)
    .get();

final userData = userDoc.data();

これは、前に保存したプロフィールを読み込む処理です。

users/{uid}
  displayName
  email
  department

ここから、チームメンバーとして保存する名前とメールを取り出します。


Step 6:displayNameとemailを作る

次のコードを確認します。

final displayName = (userData?['displayName'] ?? user.email ?? '名無し')
    .toString();

final email = (userData?['email'] ?? user.email ?? '').toString();

意味はこうです。

displayName があれば使う
↓
なければ user.email を使う
↓
それもなければ「名無し」にする

?? は、左側がなければ右側を使うという意味です。


Step 7:teams/{teamId} を作る

まず、チーム本体を作ります。

final teamRef = await FirebaseFirestore.instance.collection('teams').add({
  'name': teamName,
  'ownerId': user.uid,
  'memberIds': [user.uid],
  'createdAt': FieldValue.serverTimestamp(),
  'updatedAt': FieldValue.serverTimestamp(),
});

ここでは、teams に新しいチームを作っています。

大事なのは、この2つです。

'ownerId': user.uid,
'memberIds': [user.uid],

ownerId は、チームを作った人のIDです。

memberIds は、チームに参加している人のID一覧です。

作った本人は、最初から参加メンバーなので、memberIds に入れます。


Step 8:members/{uid} にownerとして保存する

ここがこのページの一番大事な部分です。

await teamRef.collection('members').doc(user.uid).set({
  'uid': user.uid,
  'email': email,
  'displayName': displayName,
  'role': 'owner',
  'joinedAt': FieldValue.serverTimestamp(),
});

このコードで、チーム作成者を owner として保存します。

保存先はここです。

teams/{teamId}/members/{uid}

つまり、作成したチームの中に、作成者のメンバー情報を作ります。


Step 9:role: owner の意味

この部分を見ます。

'role': 'owner',

role は、その人の権限です。

owner は、チームの所有者です。

たとえば、今後このような判断に使えます。

ownerなら
↓
メンバー追加できる
タスク削除できる
チーム設定を変更できる

viewerなら
↓
見るだけ

このように、role を保存しておくと、あとで権限管理ができます。


Step 10:完成形のcreateTeam()

createTeam() は、次の形になっていればOKです。

Future<void> createTeam(StateSetter setSheetState) async {
  final user = FirebaseAuth.instance.currentUser;

  if (user == null) {
    setSheetState(() {
      errorText = 'ログイン状態を確認できません。';
    });
    return;
  }

  final teamName = teamNameController.text.trim();

  if (teamName.isEmpty) {
    setSheetState(() {
      errorText = 'チーム名を入力してください。';
    });
    return;
  }

  setSheetState(() {
    isCreating = true;
    errorText = null;
  });

  try {
    final userDoc = await FirebaseFirestore.instance
        .collection('users')
        .doc(user.uid)
        .get();

    final userData = userDoc.data();

    final displayName =
        (userData?['displayName'] ?? user.email ?? '名無し').toString();

    final email = (userData?['email'] ?? user.email ?? '').toString();

    final teamRef = await FirebaseFirestore.instance.collection('teams').add({
      'name': teamName,
      'ownerId': user.uid,
      'memberIds': [user.uid],
      'createdAt': FieldValue.serverTimestamp(),
      'updatedAt': FieldValue.serverTimestamp(),
    });

    await teamRef.collection('members').doc(user.uid).set({
      'uid': user.uid,
      'email': email,
      'displayName': displayName,
      'role': 'owner',
      'joinedAt': FieldValue.serverTimestamp(),
    });

    if (mounted) {
      Navigator.of(context).pop();
    }
  } on FirebaseException catch (e) {
    setSheetState(() {
      errorText = e.message ?? 'チーム作成に失敗しました。';
    });
  } catch (_) {
    setSheetState(() {
      errorText = 'チーム作成に失敗しました。';
    });
  } finally {
    if (mounted) {
      setSheetState(() {
        isCreating = false;
      });
    }
  }
}

Step 11:保存する

main.dart を保存します。

Macの場合:

command + S

Windowsの場合:

Ctrl + S

Step 12:実行する

ターミナルで実行します。

flutter run

すでに起動している場合は、ターミナルで r を押します。

r

うまく反映されない場合は、R でホットリスタートします。

R

Step 13:チームを作って確認する

アプリでログインします。

右下の + ボタンを押します。

+ ボタン
↓
チーム名を入力
↓
作成する

例:

開発チーム

作成できたら、Firebase Consoleで確認します。


Step 14:Firestoreでownerを確認する

Firebase Consoleを開きます。

https://console.firebase.google.com/

次の順番で確認します。

Firestore Database
↓
データ
↓
teams
↓
作成したteamId
↓
members
↓
自分のuid

中身に、次があれば成功です。

uid
email
displayName
role: owner
joinedAt

特に見るのはここです。

role: owner

これが保存されていれば、このページのゴール達成です。


なぜowner登録が必要なのか

チームを作っただけでは、誰が管理者なのか分かりません。

そのため、作成者を owner として保存します。

チームを作る
↓
作成者をownerにする
↓
あとで権限チェックできる

たとえば、今後こういう処理ができます。

ownerだけがメンバーを追加できる
ownerだけがチーム設定を変更できる
ownerとadminだけがタスクを削除できる
viewerは見るだけ

role を保存しておくことで、チーム内のルールを作れるようになります。


よくあるエラーと直し方

エラー原因直し方
FirebaseAuth isn't definedimportがないfirebase_auth.dart をimportする
FirebaseFirestore isn't definedimportがないcloud_firestore.dart をimportする
FieldValue isn't definedimportがないcloud_firestore.dart をimportする
permission-deniedFirestore Rulesで拒否されている開発用Rulesを確認する
role が保存されないmembersset() がないteamRef.collection('members').doc(user.uid).set() を確認
rolemember になる値を間違えている'role': 'owner' にする
members が作られないteamRef のあとに保存処理がないteamRef.collection('members') を確認

permission-denied** が出たとき**

開発中だけ、Firestore Rulesを次のようにして確認できます。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

これは学習用です。

本番公開では使わないでください。

本番では、ログインユーザーやチームメンバーだけが必要なデータを読み書きできるようにします。


members** が作られないとき**

次のコードがあるか確認してください。

await teamRef.collection('members').doc(user.uid).set({
  'uid': user.uid,
  'email': email,
  'displayName': displayName,
  'role': 'owner',
  'joinedAt': FieldValue.serverTimestamp(),
});

特に、この部分が大切です。

'role': 'owner',

また、teamRef を作ったあとに書いているか確認します。

final teamRef = await FirebaseFirestore.instance.collection('teams').add({...});

await teamRef.collection('members').doc(user.uid).set({...});

この順番で書きます。


最短作業まとめ

読むのが大変な人は、ここだけ見てください。

1. チーム作成者のuidを取得

final user = FirebaseAuth.instance.currentUser;

2. teams/{teamId} を作る

final teamRef = await FirebaseFirestore.instance.collection('teams').add({
  'name': teamName,
  'ownerId': user.uid,
  'memberIds': [user.uid],
  'createdAt': FieldValue.serverTimestamp(),
  'updatedAt': FieldValue.serverTimestamp(),
});

3. members/{uid} にownerとして保存

await teamRef.collection('members').doc(user.uid).set({
  'uid': user.uid,
  'email': email,
  'displayName': displayName,
  'role': 'owner',
  'joinedAt': FieldValue.serverTimestamp(),
});

4. 保存して実行

flutter run

チェックリスト

□ main.dartを開いた
□ createTeam()を探した
□ currentUserでログイン中ユーザーを取得した
□ users/{uid} からプロフィールを取得した
□ teams/{teamId} を作った
□ ownerIdにuser.uidを入れた
□ memberIdsにuser.uidを入れた
□ members/{uid} を作った
□ roleにownerを入れた
□ joinedAtを入れた
□ 保存した
□ flutter runで起動した
□ Firestoreでrole: ownerを確認した

ミニ確認問題

Q1. チーム作成者の role は何にしますか?

回答

owner にします。

チームを作った人は、そのチームの所有者だからです。


Q2. members/{uid} には何を保存しますか?

回答

チーム内でのメンバー情報を保存します。

今回なら、uidemaildisplayNamerolejoinedAt を保存します。


Q3. なぜ ownerIdmembers/{uid} の両方に保存しますか?

回答

ownerId は、チームの作成者をすぐ確認するために使います。

members/{uid} は、チーム内での権限や表示名を管理するために使います。

役割が違うため、両方保存します。


Q4. このページでnpmや環境変数は必要ですか?

回答

必要ありません。

このページでは、チーム作成者を owner としてFirestoreに保存するだけです。


このページのまとめ

  • チームを作った人は、自動で owner として保存する。
  • owner は、チームの所有者という意味。
  • teams/{teamId} には ownerIdmemberIds を保存する。
  • teams/{teamId}/members/{uid} には role: owner を保存する。
  • role は、あとで権限管理に使う。
  • owner を保存しておくと、メンバー追加・削除・チーム管理などを制御できる。
  • このページではnpmや環境変数は不要。

次のページでやること

次のページでは、自分が参加しているチームだけを一覧表示します。

where('memberIds', arrayContains: uid) を使って、ログイン中ユーザーのチームを取得します。

教材トップへ戻る