☑️

【Flutter】TODOリストをドラッグ&ドロップできるようにする

2023/07/25に公開2

私が作っている禁酒アプリのOB-1にて、ドラッグ&ドロップでTODOリストの順番を入れ替えられるようにしたので、その方法をまとめます。

OB-1ダウンロードしてね

https://apps.apple.com/jp/app/id1666271955

ListViewからReorderableListViewへリプレイス

使ったWidgetはReorderableListViewです。

https://api.flutter.dev/flutter/material/ReorderableListView-class.html

もともとListViewだったものを、今回ReorderableListViewに入れ替えました。
そして、onReorderプロパティを追加します。

todo_list_container.dart
-   return ListView.builder(
+   return ReorderableListView.builder(
      itemCount: todayTodo.length,
      itemBuilder: (context, index) {
        final todo = todayTodo[index];
        return ListTile(
+         key: Key(index.toString()), // FIXME: zennのコメント参照。Key(todo.id)の方が良さそう
          title: Text(
            todo.title,
          ),
        );
      },
+     onReorder: (int oldIndex, int newIndex) async {
+       if (oldIndex < newIndex) {
+         newIndex -= 1;
+       }
+       final Todo todo = todayTodo.removeAt(oldIndex);
+       todayTodo.insert(newIndex, todo);
+
+       await todoRepo.updateIndex(
+         todos: todayTodo,
+       );
+     },
    );

onReorderはドラッグ&ドロップで入れ替えが起こった時に呼ばれるので、そこで入れ替えた時に行いたい処理を書いています。

Firestoreのバッジ書き込みによる更新

OB-1の場合、以下のコードによりFirestoreを更新しています。

todo_list_container.dart
await todoRepo.updateIndex(
  todos: todayTodo,
);

これは、私が作った関数で、下記のようなコードになっています。

todo_repositroy.dart
  Future<void> updateIndex({
    required List<Todo> todos,
  }) async {
    final batch = FirebaseFirestore.instance.batch();

    todos.asMap().forEach((index, todo) {
      batch.update(
        todoListCollection.doc(todo.id),
        {
          "index": index,
          "updatedAt": Timestamp.now(),
        },
      );
    });
    await batch.commit();
  }

同時に複数のTODOオブジェクトを更新するので、バッジ書き込みを行っています。下記は公式ドキュメントなので細かい仕様を確認してみてください。

https://firebase.google.com/docs/firestore/manage-data/transactions?hl=ja

先ほど黄色いボックスの中で紹介しましたが、リストを表示する前に、以下のようなsortが入っているので、indexの数字の順番に表示されるというわけです。

todo_repositroy.dart
list.sort((a, b) => a.index.compareTo(b.index));

まとめ

という感じで、以下のような入れ替え機能をv1.4.0で実装できました!

OB-1ダウンロードしてね!

https://apps.apple.com/jp/app/id1666271955

GitHubリポジトリで全コード見れます。

Flutter大学に入っているかたはリポジトリが見れます。
https://github.com/flutteruniv/ob-1

Flutter大学はこちら
https://flutteruniv.com/

Flutter大学

Discussion

Kosuke SaigusaKosuke Saigusa

改善案の提案です!

通常 ListTilekey としてインデックスを使用するのは避けるべきだと思われます。リストが並べ替えられたり、項目が追加・削除されたりすると、同じインデックスが異なる項目に割り当てられて、一意性が破られる可能性があるからです。

よって todo.id のように真に一意な値を key として使用するべきだと思います。

kboykboy

コメントありがとうございます!
ネットで見たサンプルコードのまま書いてました、、、!
その通りだと思います :bow: