😁

Flutter ReorderableListView

に公開

ReorderableListView

上記を業務で使用することになったので備忘録でかきこ。

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

背景

APIの返却値でリストが渡されるのだが、このリストの中身をドラッグで入れ替えしたい。

よくアプリでみる「長押しで順番入れ替える」要素を導入するためにReorderableListViewを利用することになりました。

またドラッグで要素を移動する際に、ドラッグした要素を角丸にしたい要件もあり、そちらも対応。

FlutterのHooksを利用しているため、Hooks対応のコードになります。

実際のコード

final card = useState(hogehogeList);

              Flexible(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 7),
                  child: ReorderableListView.builder(
                    itemCount: card.value.length,
                    proxyDecorator: (child, index, animation) {
                      // ドラッグ中の見た目
                      // 角丸に変更 
                      return Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(8),
                          boxShadow: [
                            BoxShadow(
                              color:
                                  ColorConstants.black000000.withOpacity(0.25),
                              blurRadius: 4,
                            ),
                          ],
                        ),
                        clipBehavior: Clip.antiAlias,
                        child: child,
                      );
                    },
                    itemBuilder: (context, index) {
                      return Material(
                        key: ValueKey(card.value[index].cardId),
                        child: cardArea(index, card.value[index]),
                      );
                    },
                    onReorder: (oldIndex, newIndex) {
                      /// ドラッグした際に順番変わる
                      if (oldIndex < newIndex) {
                        newIndex -= 1;
                      }

                      /// 新規のリストを作成してからstateを更新する
                      final next = List<hogehoge>.of(card.value);
                      final item = next.removeAt(oldIndex);
                      next.insert(newIndex, item);
                      card.value = next;

            /// 入れ替え後API処理 or 別の場所で処理
                    },
                  ),
                ),
              ),

重要部分

 proxyDecorator: (child, index, animation) {
                      // ドラッグ中の見た目
                      // 角丸に変更 
                      return Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(8),
                          boxShadow: [
                            BoxShadow(
                              color:
                                  ColorConstants.black000000.withOpacity(0.25),
                              blurRadius: 4,
                            ),
                          ],
                        ),
                        clipBehavior: Clip.antiAlias,
                        child: child,
                      );
                    },

proxyDecoratorは、下記の通りドラッグ中のUI変更部分を担います。

A callback that allows the app to add an animated decoration around an item when it is being dragged.


  onReorder: (oldIndex, newIndex) {
                      /// ドラッグした際に順番変わる
                      if (oldIndex < newIndex) {
                        newIndex -= 1;
                      }

                      /// 新規のリストを作成してからstateを更新する
                      final next = List<hogehoge>.of(card.value);
                      final item = next.removeAt(oldIndex);
                      next.insert(newIndex, item);
                      card.value = next;

            /// 入れ替え後API処理 or 別の場所で処理
                    },

上記がリストの入れ替えを担っています。
画面描画をuseStateで判断していましたが、リストをそもそも入れ替えないと再描画されませんでした。そのため、上記のような実装になっています。」といった表現を検討してみてください。

その他機能

  buildDefaultDragHandles: false,

ReorderableListView に 'buildDefaultDragHandles' なるプロパティがあります。
trueの場合要素全体を長押しするとドラッグできるようになります。

逆にfalseだと上記ができなくなります。

falseにして特定の場所を長押しでドラッグしたい場合は下記のように'ReorderableDelayedDragStartListener' をつけてあげてください。

ListTile(
            key: ValueKey(item),
            title: Text(item),
            // 右端アイコンだけでドラッグ開始(長押し)
            trailing: ReorderableDelayedDragStartListener(
              index: index,
              child: const Icon(Icons.drag_handle),
            ),
          );

そうするとListTileの二本線のアイコン部分でドラッグすることができます。
上記のやり方がわからずまぁまぁハマりましたね、、、、

ReorderableListView.builder or ReorderableListView

.builderがあるかないかがありますが、僕は使えるなら基本的にアプリのパフォーマンスを考えると.builderがある方を使った方がいいかと思います。
理由は下記で説明してます。

https://zenn.dev/rionishino/articles/c4041f0073c151

以上!!

Discussion