0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】ReorderableListView で買い物リストを並び替える

0
Posted at

ReorderableListView の基本

Flutter でリストのドラッグ&ドロップによる並び替えを実装するには、ReorderableListView を使用します。

基本的な実装

ReorderableListView(
  onReorderItem: (oldIndex, newIndex) {
    HapticFeedback.lightImpact(); // 触覚フィードバック

    setState(() {
      final item = _items.removeAt(oldIndex);
      _items.insert(newIndex, item);
    });
  },
  children: [
    for (final item in _items)
      ListTile(
        key: ValueKey(item.id),
        title: Text(item.name),
        leading: const Icon(Icons.drag_handle),
      ),
  ],
)

onReorderItem で移動元と移動先のインデックスを受け取り、_items の要素を並べ替えます。

従来の onReorder とは異なり、onReorderItem では newIndex の補正が自動で行われるため、手動で newIndex-- をする必要はありません。

onReorder を使用する場合の index 補正

従来の onReorder を使用する場合は、移動元の要素を削除するとリストの要素数が1つ減るため、移動先のインデックスを補正する必要があります。

onReorder: (oldIndex, newIndex) {
  setState(() {
    if (newIndex > oldIndex) {
      newIndex--;
    }

    final item = _items.removeAt(oldIndex);
    _items.insert(newIndex, item);
  });
},

移動方向ごとの補正は次のとおりです。

移動方向 インデックスの補正
上方向 不要
下方向 newIndex-- が必要

※ この補正は従来の onReorder を使用する場合のものです。onReorderItem では不要です。

各アイテムに一意な key を指定する

ReorderableListView の各子 Widget には key が必要です。

ListTile(
  key: ValueKey(item.id),
  title: Text(item.name),
)

ValueKey(item.id) のように、各アイテムを一意に識別できる値を指定します。

item.id が重複していると、正しくアイテムを識別できないため注意が必要です。

空リストの表示

リストが空の場合は、並び替え用の UI ではなく、空の状態を示す Widget を表示すると親切です。

body: _items.isEmpty
    ? const EmptyView(
        title: 'アイテムがありません',
        message: 'アイテムを追加してから並び替えてください',
      )
    : ReorderableListView(
        onReorderItem: (oldIndex, newIndex) {
          setState(() {
            final item = _items.removeAt(oldIndex);
            _items.insert(newIndex, item);
          });
        },
        children: [
          for (final item in _items)
            ListTile(
              key: ValueKey(item.id),
              title: Text(item.name),
            ),
        ],
      ),

EmptyView は独自に定義した Widget を想定しています。

確定ボタンを押すまで親画面に変更を反映しない

並び替え画面で編集した結果を、確定ボタンを押したタイミングで親画面に返す設計もできます。

FilledButton(
  onPressed: () => Navigator.pop(context, _items),
  child: const Text('確定'),
)

Navigator.pop(context, _items) で画面を閉じ、並び替え後のリストを呼び出し元に返します。

このコード自体は Firestore への保存を行いません。保存が必要な場合は、親画面で返されたリストを受け取り、Firestore への保存処理を実行します。

このようにすることで、並び替え中はローカルのリストのみを更新し、確定時に結果を反映する設計にできます。

ポイント

  • ReorderableListView:ドラッグ&ドロップによる並び替えを実現する Widget
  • ValueKey(item.id):各アイテムを一意に識別するための key
  • onReorderItem:並び替え後のインデックスを受け取るコールバック
  • HapticFeedback.lightImpact():軽い触覚フィードバックを発生させる
  • setState:並び替え後に UI を再描画する
  • Navigator.pop(context, _items):編集結果を親画面に返す

まとめ

ReorderableListView と一意な key を組み合わせることで、ドラッグ&ドロップによるリストの並び替えを実装できます。

また、並び替え結果をローカルで管理し、確定時に親画面へ返すことで、画面上の編集とデータの保存処理を分離できます。

なお、onReorderItem は Flutter 3.44 で安定版に導入された API です。それ以前の Flutter バージョンでは、従来の onReorder を使用してください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?