Flutter ReorderableListView
ReorderableListView
上記を業務で使用することになったので備忘録でかきこ。
背景
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がある方を使った方がいいかと思います。
理由は下記で説明してます。
以上!!
Discussion