4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Flutter】各要素を並べ替えできる ListView を使ってみる

Posted at

成果物

デモ

Web版だとReorderableListViewのみ挙動が異なります。
(右側の並び替えアイコンを長押ししないと並び替えできない。)

ソースコード

要素を並び替えられる ListView

デフォルトで入っているReorderableListView&SliverReorderableListを使います。

ReorderableListView の使い方

  • 基本的にはListViewと同じ使い方
  • itemBuilderには並べたい要素を生成する関数を指定します。
  • ListViewと異なる点としては、各要素にユニークなKeyを指定する必要があることです。
  • onReorderには並び替えが完了したとき(ドラッグしている要素を放したとき)に行う処理を記載します。
  • proxyDecoratorは任意に指定可能で、ドラッグしている要素をデコレーションできます(今回のコードでは、ドラッグする要素を半透明にしています。
余談

VueやReactのリストでもkeyを指定する必要があり、keyにはindexは使わないほうがいいみたいなのをどこかで見た気がするが、Flutter公式サンプルがindexを使っていたので使っても大丈夫そう。

reordable_list_view_page.dart
class ReorderableListViewPage extends ConsumerWidget {
  const ReorderableListViewPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final items = ref.watch(itemsProvider);

    return Scaffold(
      appBar: AppBar(),
      body: ReorderableListView.builder(
        itemBuilder: (_, index) => ItemTile(
          items[index],
          key: Key('$index'), // 各要素にユニークなKeyをつける必要がある
        ),
        itemCount: items.length,
        onReorder: (int oldIndex, int newIndex) {
          _onReorder(items, oldIndex, newIndex);
        },
        proxyDecorator: (widget, _, __) {
          return Opacity(opacity: 0.5, child: widget);
        },
      ),
    );
  }

  void _onReorder(List<Item> items, int oldIndex, int newIndex) {
    if (oldIndex < newIndex) {
      newIndex -= 1;
    }
    items.insert(newIndex, items.removeAt(oldIndex));
  }
}

ReorderableListView(children: [])という書き方もできます。

SliverReorderableList の使い方

  • ReorderableListViewと異なり、並べたい要素をReorderableDragStartListenerまたはReorderableDelayedDragStartListenerでラップする必要があります。
  • その影響で、ユニークなkeyを付与する対処も異なるので注意。
            itemBuilder: (_, index) => ReorderableDelayedDragStartListener(
              index: index,
              // ItemTileではなく、ReorderableDelayedDragStartListenerにkeyを付与
              key: Key('$index'), 
              child: ItemTile(items[index]),
            ),
  • ReorderableDragStartListenerReorderableDelayedDragStartListenerの違いはドラッグ開始までのタップ時間。ReorderableDragStartListenerはほぼタップした瞬間にドラッグが始まりますが、ReorderableDelayedDragStartListenerは長押ししたらドラッグが始まります。
  • ReorderableDragStartListenerは画面全体のスクロールがしづらくなってしまうため、要素が多くスクロールが必要な場合はReorderableDelayedDragStartListenerを使うのがおすすめです。
  • ReorderableListViewと同様、proxyDecoratorを指定することでドラッグ中の要素をデコレーションできます。
class SliverReorderableListPage extends ConsumerWidget {
  const SliverReorderableListPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final items = ref.watch(itemsProvider);

    return Scaffold(
      body: CustomScrollView(
        slivers: [
          const SliverAppBar(),
          SliverReorderableList(
            // ReorderableListViewと異なり、
            // 各要素を ReorderableDragStartListener または ReorderableDelayedDragStartListener
            // でラップする必要がある。
            // その影響もあり、keyを付与する対象は ReorderableDragStartListener または
            // ReorderableDragStartListenerになるので注意。
            //
            // ReorderableDragStartListenerはタップで移動が開始されるが、
            // ReorderableDelayedDragStartListenerはロングタップで移動開始となる。
            // タップで要素の移動が始まってしまうと
            // スクロールがしづらくなるので ReorderableDelayedDragStartListener のほうがいい
            itemBuilder: (_, index) => ReorderableDelayedDragStartListener(
              index: index,
              key: Key('$index'),
              child: ItemTile(items[index]),
            ),
            itemCount: items.length,
            onReorder: (int oldIndex, int newIndex) {
              _onReorder(items, oldIndex, newIndex);
            },
            proxyDecorator: (widget, _, __) {
              return Opacity(opacity: 0.5, child: widget);
            },
          ),
        ],
      ),
    );
  }

  void _onReorder(List<Item> items, int oldIndex, int newIndex) {
    if (oldIndex < newIndex) {
      newIndex -= 1;
    }
    items.insert(newIndex, items.removeAt(oldIndex));
  }
}
4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?