☑️
【Flutter】TODOリストをドラッグ&ドロップできるようにする
私が作っている禁酒アプリのOB-1にて、ドラッグ&ドロップでTODOリストの順番を入れ替えられるようにしたので、その方法をまとめます。
OB-1ダウンロードしてね
ListViewからReorderableListViewへリプレイス
使ったWidgetはReorderableListViewです。
もともと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オブジェクトを更新するので、バッジ書き込みを行っています。下記は公式ドキュメントなので細かい仕様を確認してみてください。
先ほど黄色いボックスの中で紹介しましたが、リストを表示する前に、以下のようなsortが入っているので、indexの数字の順番に表示されるというわけです。
todo_repositroy.dart
list.sort((a, b) => a.index.compareTo(b.index));
まとめ
という感じで、以下のような入れ替え機能をv1.4.0で実装できました!
OB-1ダウンロードしてね!
GitHubリポジトリで全コード見れます。
Flutter大学に入っているかたはリポジトリが見れます。
Flutter大学はこちら
Discussion
改善案の提案です!
通常
ListTile
のkey
としてインデックスを使用するのは避けるべきだと思われます。リストが並べ替えられたり、項目が追加・削除されたりすると、同じインデックスが異なる項目に割り当てられて、一意性が破られる可能性があるからです。よって
todo.id
のように真に一意な値をkey
として使用するべきだと思います。コメントありがとうございます!
ネットで見たサンプルコードのまま書いてました、、、!
その通りだと思います :bow: