ドラッグ&ドロップで並べ替え可能(Reorderable)なGridViewを実装しようとしたときのメモ
ReorderableListView
は標準で実装されているがReorderableGridView
は現状ないので、pub.dev
でパッケージを調査。
関連パッケージとしては、以下3つのパッケージ(+1ソースコード)が見つかった。
(他にあったら教えてください )
パッケージ化はされていないようだが、順番が入れ替わる際のアニメーションがキレイなソースコード
GitHub - spkersten/sliver_draggables
それぞれ実際に使ってみて、気づいた点を書いていく。
まとめ
評価軸 | 説明 |
---|---|
サンプルコード量 | pubdevに乗っているソースコードの量 |
カスタマイズ性 | メインのWidgetのパラメータの多さ |
ドラッグ中の要素入れ替え | ドラッグ中に、ドラッグ位置に応じて要素が入れ替わるかどうか |
アニメーション | ドラッグ中の要素入れ替えがアニメーション付きで行われるかどうか |
オートスクロール | ドラッグしている要素の位置が画面上端・下端に来たときに自動でスクロールされるかどうか |
パッケージ | サンプルコード量(pubdev) | カスタマイズ性 | ドラッグ中の要素入れ替え | アニメーション | オートスクロール |
---|---|---|---|---|---|
drag_and_drop_gridview | ○ | ○ | ○ | × | △ |
reorderableitemsview | △ | △ | × | × | ○ |
reorderables ※ | ○ | ○ | ○ | ○ | × |
sliver_draggables | △ | △ | ○ | ○ | × |
※ GridViewは未サポートだが、要素の幅を指定することでGridViewっぽく見せることが可能。常に正常に動くかは不明。
実装難易度的にはアニメーション>オートスクロールだと思うので、sliver_draggables
にオートスクロールを追加できるのがベストだと考える。
アニメーションやドラッグ中の要素移動はないが、動作が安定していそうなのはreorderableitemsview
。
スクロールの不具合を直せるならdrag_and_drop_gridview
も選択肢に入ってくる。
drag_and_drop_gridview
まさにやりたいことを実現してくれているパッケージ。
GridView
と同じパラメータを扱っているため後から導入しやすい。- 並び替えたい要素をドラッグしている際中に、ドラッグ位置に応じて並び方が変わるサンプルがあるので、実装にもそんなに困らない。
- ドラッグ中に、ドラッグ元となった要素を変えたり(
childWhenDragging
)、ドラッグ中だけドラッグしている要素を変えるためのフィードバック(feedback
)を変えられるのでカスタマイズ性が高い。 - 要素が並べ替わる際のアニメーションがない。
- ドラッグ位置が上端or下端になったときのオートスクロールの挙動が怪しい。
- 関数を指定できるプロパティ(
onWillAccept
など)に指定する関数の要件がパッと見わからないので、間違っていると謎のエラーに遭遇する。(例:onWillAccept
はbool Function(int, int)
を渡さないといけないが、Function
としか書いてない)
要素が並べ替わる際のアニメーションはないが、onWillAccept
の実装によってはドラッグ中に並べ替わる様子を表現はできるので、スクロールの部分さえ直せればかなり良い。
reorderableitemsview
flutter_staggered_grid_viewという GridViewの各セルを自由な大きさにできるパッケージを並べ替え可能にしたパッケージ。各セルの大きさを同じにすればやりたいことができる。
- ドラッグ位置が上端or下端になったときのオートスクロールは◎
- ドラッグ開始のトリガーが長押しか普通のタップかどうかを選べる。(
longPressToDrag
) onWillAccept
パラメータが指定できないので、ドラッグ中の処理を定義できない。- 上記故に、ドラッグ中にドラッグ位置に応じて各要素を並び替えられない。
- 要素が入れ替わる際のアニメーションはない。
ドラッグ中に各要素が入れ替わる演出を気にしなければこれでOK。
reorderables
GridView
には対応していないが、ReorderableWrap
Widgetを使って並べる要素の幅/高さをMediaQuery.of(context).size.width / 2,
とかで指定すればそれっぽくはなる。
- ドラッグ位置が上端or下端になったときのオートスクロール機能はない。
- ドラッグ開始のトリガーが長押しか普通のタップかどうかを選べる。(
longPressToDrag
) - ドラッグ中に各要素が入れ替わるアニメーションがある。
- トリッキーな方法でGridっぽくしているため、不安がある
コード例
@override Widget build(BuildContext context) { List<Widget> _tiles = List.generate( imageUris.length, (int index) => Container( width: MediaQuery.of(context).size.width / 2, height: MediaQuery.of(context).size.width / 2, child: Card( child: Image.network( imageUris[index], fit: BoxFit.cover, ), ), ), ); return Scaffold( appBar: AppBar(), body: ListView( controller: _scrollController, children: [ ReorderableWrap( children: _tiles, onReorder: (int oldIndex, int newIndex) { print('$newIndex'); setState(() { final String oldUri = imageUris.removeAt(oldIndex); imageUris.insert(newIndex, oldUri); }); }, ), ], ), ); }
sliver_draggables
下記のIssueから見つけたソースコード。 RenderSliverGrid assert on SliverGridLayout too strict · Issue #28056 · flutter/flutter · GitHub
- ドラッグ中のアニメーションは完璧。
- オートスクロールはない。
SliverLongPressReorderableGrid
に渡せるパラメーターが少ないのでカスタマイズ性は低そう。
スクロールなしで収まる量の要素を並び替えるのであれば、これが一番キレイ。