Open3

FlutterのReorderableListViewの代わりにImplicitlyAnimatedReorderableListを使う

YuheiNakasakaYuheiNakasaka

ReorderableListViewはドラッグ&ドロップでListView内のアイテムの順番を入れ替えたりするための標準Widget。

簡単な並び替えがやりたいだけならこれでも十分なのだが、用件によってはこれでは実現できないこともある。

例えばドラッグ中にアイテムの色を変えたいなど。

そこで今回はimplicitly_animated_reorderable_listを使ってそうした用件を満たす実装をしてみる。

https://pub.dev/packages/implicitly_animated_reorderable_list

YuheiNakasakaYuheiNakasaka

公式のドキュメントには簡単な説明と雑なexampleしかないので最初は戸惑うかもしれないけど、基本はこんな感じでいける。Reorderable()Material()Handle()をラップしてそれをImplicitlyAnimatedReorderableListitemBuilderでreturnすればok。あとは迷いそうなところにコメントを付けたので読めばわかるはず。

  int _draggingItemId = -1; // 適当に-1で初期化してる

  ImplicitlyAnimatedReorderableList<MyItem>(
      // ここの型はitemsの中身の型を指定
      items: items,
      areItemsTheSame: (oldItem, newItem) => oldItem.id == newItem.id,
      // D&Dでドラッグが始まった時の処理。ここでドラッグ中のidをセットしてる。
      onReorderStarted: (item, index) {
        setState(() => _draggingItemId = item.id);
      },
      // D&Dでドラッグが終わった時の処理。
      onReorderFinished: (item, from, to, newItems) {
        // ここでドラッグ中のidを初期化してる
        setState(() => _draggingItemId = -1);
        // itemsの状態更新処理。setStateでitemsを更新するとかここは色々
      },
      itemBuilder: (context, itemAnimation, item, index) {
        return Reorderable(
          // Keyは必須
          key: ValueKey(item),
          builder: (context, dragAnimation, inDrag) {
            // ドラッグ中のアニメーションを記述できる
            final t = dragAnimation.value;
            final elevation = lerpDouble(0, 8, t);
            final color =
                Color.lerp(Colors.white, Colors.white.withOpacity(0.8), t);
            // Transitionを付けたくないならこれはなくても良い。Material(...)以下をreturnすれば良い。
            return SizeFadeTransition(
              sizeFraction: 0.7,
              curve: Curves.easeInOut,
              animation: itemAnimation,
              child: Material(
                color: color,
                elevation: elevation,
                // ドラッグ中の背景を透明にできる
                type: MaterialType.transparency,
                child: ListTile(
                  // ドラッグ中はgreyにしてる
                  tileColor: _draggingItemId == item.id ? Colors.grey : Colors.white,
                  title: Text(item.name),
                  // Handleがドラッグ&ドロップを行うためのWidget。これがないとアイテムが動かないので忘れず使う。
                  trailing: Handle(
                    delay: const Duration(
                        milliseconds:
                            100), // どのくらいの時間アイテムをタップしたらソート可能な状態になるかを指定できる
                    child: Icon(
                      Icons.list,
                      color: Colors.grey,
                    ),
                  ),
                ),
              ),
            );
          },
        );
      },
    );
YuheiNakasakaYuheiNakasaka

全体的に冗長な感じなので初見だとダルいと感じるけど、標準のReorderableListViewでは痒いところに手が届かなすぎるので仕方ない。