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

【権限判定】canCreateTask・canEditTask・canDeleteTaskを作る

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

このページでやること

このページでは、ログイン中の人が「タスクを作れるか・編集できるか・削除できるか」を判定する関数を作ります。

使う関数は、この3つです。

canCreateTask()
canEditTask()
canDeleteTask()

権限は、前のページで作った TeamRole を使います。

owner
admin
member
viewer

今日のゴール

次のように、権限ごとにできる操作を分けます。

roleタスク作成タスク編集タスク削除
owner
admin
member×
viewer×××

つまり、ルールはこうです。

owner  → 作成・編集・削除できる
admin  → 作成・編集・削除できる
member → 作成・編集できる。削除はできない
viewer → 見るだけ

npmや環境変数は必要?

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

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

main.dart にDartの関数を追加するだけです。

main.dartを開く
↓
TeamRoleを確認する
↓
canCreateTaskを作る
↓
canEditTaskを作る
↓
canDeleteTaskを作る
↓
画面の操作に使う

Step 1:main.dartを開く

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

lib/main.dart

ターミナルから開く場合はこちらです。

code lib/main.dart

Step 2:TeamRole enumを確認する

前のページで作った TeamRole があるか確認します。

enum TeamRole {
  owner,
  admin,
  member,
  viewer,
}

これがない場合は、先に追加します。

TeamRole は、チーム内の権限を表す型です。


Step 3:canCreateTaskを作る

まず、タスクを作成できるかを判定します。

TeamRole の下あたりに、次の関数を追加します。

bool canCreateTask(TeamRole role) {
  return role == TeamRole.owner ||
      role == TeamRole.admin ||
      role == TeamRole.member;
}

意味はこうです。

owner  → 作成できる
admin  → 作成できる
member → 作成できる
viewer → 作成できない

viewer は見るだけなので、タスク作成はできません。


Step 4:canEditTaskを作る

次に、タスクを編集できるかを判定します。

bool canEditTask(TeamRole role) {
  return role == TeamRole.owner ||
      role == TeamRole.admin ||
      role == TeamRole.member;
}

意味はこうです。

owner  → 編集できる
admin  → 編集できる
member → 編集できる
viewer → 編集できない

最初の設計では、member もタスク編集できるようにします。


Step 5:canDeleteTaskを作る

次に、タスクを削除できるかを判定します。

bool canDeleteTask(TeamRole role) {
  return role == TeamRole.owner || role == TeamRole.admin;
}

意味はこうです。

owner  → 削除できる
admin  → 削除できる
member → 削除できない
viewer → 削除できない

削除は強い操作なので、owneradmin だけにします。


Step 6:3つの関数の完成形

ここまでの完成形です。

bool canCreateTask(TeamRole role) {
  return role == TeamRole.owner ||
      role == TeamRole.admin ||
      role == TeamRole.member;
}

bool canEditTask(TeamRole role) {
  return role == TeamRole.owner ||
      role == TeamRole.admin ||
      role == TeamRole.member;
}

bool canDeleteTask(TeamRole role) {
  return role == TeamRole.owner || role == TeamRole.admin;
}

この3つを作っておくと、画面側のコードが読みやすくなります。


Step 7:なぜ関数にするのか

毎回このように書くと、コードが長くなります。

if (role == TeamRole.owner ||
    role == TeamRole.admin ||
    role == TeamRole.member) {
  // タスク作成OK
}

関数にすると、短く書けます。

if (canCreateTask(role)) {
  // タスク作成OK
}

読みやすくなります。

canCreateTask(role)
↓
この人はタスクを作れる?

Step 8:getMyRoleを確認する

自分の権限を取得する関数があるか確認します。

Future<TeamRole> getMyRole() async {
  final user = FirebaseAuth.instance.currentUser;

  if (user == null) {
    return TeamRole.viewer;
  }

  final memberDoc = await FirebaseFirestore.instance
      .collection('teams')
      .doc(widget.teamId)
      .collection('members')
      .doc(user.uid)
      .get();

  final data = memberDoc.data();
  final roleText = data?['role']?.toString();

  return teamRoleFromString(roleText);
}

この関数で、Firestoreの role を取得します。

見る場所はこちらです。

teams/{teamId}/members/{uid}

Step 9:タスク作成ボタンで使う

タスク作成は、右下の + ボタンで行います。

今はこのようになっているはずです。

floatingActionButton: FloatingActionButton(
  onPressed: showCreateTaskSheet,
  child: const Icon(Icons.add),
),

これを、権限チェック付きに変更します。

floatingActionButton: FloatingActionButton(
  onPressed: () async {
    final role = await getMyRole();

    if (!canCreateTask(role)) {
      showNoPermissionMessage();
      return;
    }

    showCreateTaskSheet();
  },
  child: const Icon(Icons.add),
),

これで、viewer はタスク作成できなくなります。


Step 10:タスク編集で使う

タスクをタップしたときの編集処理にも、権限チェックを入れます。

今はこのようになっているはずです。

onTap: () {
  showEditTaskSheet(
    taskId: doc.id,
    currentTitle: title,
    currentDescription: description,
    currentAssigneeEmail: assigneeEmail,
    currentPriority: priority,
    currentStatus: status,
  );
},

これを次のように変更します。

onTap: () async {
  final role = await getMyRole();

  if (!canEditTask(role)) {
    showNoPermissionMessage();
    return;
  }

  showEditTaskSheet(
    taskId: doc.id,
    currentTitle: title,
    currentDescription: description,
    currentAssigneeEmail: assigneeEmail,
    currentPriority: priority,
    currentStatus: status,
  );
},

これで、viewer は編集できなくなります。


Step 11:タスク削除で使う

タスク削除は、左スワイプの confirmDismiss で判定します。

今はこのようになっているはずです。

confirmDismiss: (_) async {
  final role = await getMyRole();

  if (!canDeleteTask(role)) {
    showNoPermissionMessage();
    return false;
  }

  return confirmDeleteTask(title);
},

この形になっていればOKです。

流れはこうです。

左スワイプ
↓
自分のroleを確認
↓
削除できる権限か確認
↓
OKなら削除確認
↓
NGなら削除しない

Step 12:権限なしメッセージを用意する

権限がないときに出すメッセージも確認します。

void showNoPermissionMessage() {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(
      content: Text('この操作を行う権限がありません。'),
    ),
  );
}

この関数は、_TaskListPageState の中に置きます。


Step 13:保存する

main.dart を保存します。

Macの場合:

command + S

Windowsの場合:

Ctrl + S

Step 14:実行する

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

flutter run

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

r

うまく反映されない場合は、R を押します。

R

Step 15:ownerで確認する

Firebase Consoleで、自分の role を確認します。

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

role が次ならOKです。

role: owner

アプリで確認します。

タスク作成 → できる
タスク編集 → できる
タスク削除 → できる

Step 16:adminで確認する

Firestoreで role を次に変更します。

role: admin

アプリで確認します。

タスク作成 → できる
タスク編集 → できる
タスク削除 → できる

Step 17:memberで確認する

Firestoreで role を次に変更します。

role: member

アプリで確認します。

タスク作成 → できる
タスク編集 → できる
タスク削除 → できない

左スワイプ削除をしようとして、権限なしメッセージが出ればOKです。


Step 18:viewerで確認する

Firestoreで role を次に変更します。

role: viewer

アプリで確認します。

タスク作成 → できない
タスク編集 → できない
タスク削除 → できない

右下の + を押したり、タスクをタップしたりして、権限なしメッセージが出ればOKです。


よくあるエラーと直し方

エラー原因直し方
canCreateTask isn't defined関数がないcanCreateTask() を追加
canEditTask isn't defined関数がないcanEditTask() を追加
canDeleteTask isn't defined関数がないcanDeleteTask() を追加
getMyRole isn't defined自分の権限取得関数がないgetMyRole() を追加
TeamRole isn't definedenumがないenum TeamRole を追加
viewerでも作成できるFABに権限チェックがないcanCreateTask(role) を入れる
viewerでも編集できるonTapに権限チェックがないcanEditTask(role) を入れる
memberでも削除できるconfirmDismissに権限チェックがないcanDeleteTask(role) を入れる

注意:Flutter側だけでは本番の安全対策にならない

このページでは、Flutter側で操作を止めています。

これは、画面上の使いやすさには必要です。

ただし、本番ではFirestore Security Rulesにも必ず書きます。

Flutter側
↓
ボタンを押せないようにする
操作時にメッセージを出す

Firestore Rules側
↓
本当にデータを守る

本番では、Rulesが最終防衛ラインです。


最短作業まとめ

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

1. 権限判定を作る

bool canCreateTask(TeamRole role) {
  return role == TeamRole.owner ||
      role == TeamRole.admin ||
      role == TeamRole.member;
}

bool canEditTask(TeamRole role) {
  return role == TeamRole.owner ||
      role == TeamRole.admin ||
      role == TeamRole.member;
}

bool canDeleteTask(TeamRole role) {
  return role == TeamRole.owner || role == TeamRole.admin;
}

2. 作成ボタンで使う

onPressed: () async {
  final role = await getMyRole();

  if (!canCreateTask(role)) {
    showNoPermissionMessage();
    return;
  }

  showCreateTaskSheet();
},

3. 編集タップで使う

onTap: () async {
  final role = await getMyRole();

  if (!canEditTask(role)) {
    showNoPermissionMessage();
    return;
  }

  showEditTaskSheet(
    taskId: doc.id,
    currentTitle: title,
    currentDescription: description,
    currentAssigneeEmail: assigneeEmail,
    currentPriority: priority,
    currentStatus: status,
  );
},

4. 削除で使う

confirmDismiss: (_) async {
  final role = await getMyRole();

  if (!canDeleteTask(role)) {
    showNoPermissionMessage();
    return false;
  }

  return confirmDeleteTask(title);
},

チェックリスト

□ main.dartを開いた
□ TeamRole enumを確認した
□ canCreateTask()を作った
□ canEditTask()を作った
□ canDeleteTask()を作った
□ getMyRole()を確認した
□ showNoPermissionMessage()を用意した
□ FloatingActionButtonにcanCreateTaskを入れた
□ TaskCardのonTapにcanEditTaskを入れた
□ DismissibleのconfirmDismissにcanDeleteTaskを入れた
□ ownerで作成・編集・削除できることを確認した
□ adminで作成・編集・削除できることを確認した
□ memberで削除できないことを確認した
□ viewerで作成・編集・削除できないことを確認した

ミニ確認問題

Q1. タスク作成できるroleはどれですか?

回答

owneradminmember です。


Q2. タスク編集できるroleはどれですか?

回答

owneradminmember です。


Q3. タスク削除できるroleはどれですか?

回答

owneradmin です。


Q4. viewerは何ができますか?

回答

タスクを見るだけです。

作成・編集・削除はできません。


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

回答

必要ありません。

Dartの関数を追加するだけです。


このページのまとめ

  • canCreateTask() でタスク作成できるか判定する。
  • canEditTask() でタスク編集できるか判定する。
  • canDeleteTask() でタスク削除できるか判定する。
  • owneradmin は作成・編集・削除できる。
  • member は作成・編集できるが、削除できない。
  • viewer は見るだけ。
  • Flutter側の判定は使いやすさのために必要。
  • 本番ではFirestore Security Rulesにも必ず権限制御を書く。
  • このページではnpmや環境変数は不要。

次のページでやること

次のページでは、メンバー一覧画面を作ります。

teams/{teamId}/members を取得して、チームに参加しているユーザーを表示します。

教材トップへ戻る