前面我們對於 ListView
的操作講過 Flutter 滑動刪除最佳實踐,那現在我們來了解一下 ListView
的拖拽排序。
效果如下:
ReorderableListView
想要達到如上效果,需使用該類,官網簡介:
A list whose items the user can interactively reorder by dragging.
This class is appropriate for views with a small number of children because constructing the List requires doing work for every child that could possibly be displayed in the list view instead of just those children that are actually visible.
All children must have a key.
簡單翻譯如下:
使用者可以通過拖動來重新排序的列表。
該類適用於少量 children 的頁面,因為構造列表需要為每一個 children 執行操作,而不只是可見的 children。
所有的 children 都必須有一個 key。
建構函式
按照慣例,檢視建構函式:
ReorderableListView({
this.header,
@required this.children,
@required this.onReorder,
this.scrollDirection = Axis.vertical,
this.padding,
this.reverse = false,
}) : assert(scrollDirection != null),
assert(onReorder != null),
assert(children != null),
assert(
children.every((Widget w) => w.key != null),
'All children of this widget must have a key.',
);
複製程式碼
瞭解一下各個引數:
- header:是一個不參與拖動排序的 Widget
- children:不用多說,列表項
- onReorder:見名知意,重新排序後的回撥
- scrollDirection:方向
剩下兩個就不多說了,都應該瞭解。
簡單使用
既然看完了建構函式,那我們就可以分分鐘寫一個 Demo 出來:
class _ReorderableListViewPageState extends State<ReorderableListViewPage> {
List<Color> _data = [
Colors.blue,
Colors.pinkAccent,
Colors.deepPurple,
Colors.orangeAccent
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ReorderableListViewPage'),
),
body: ReorderableListView(
header: Container(
height: 50,
),
children: _data
.map((s) => Card(
color: s,
key: Key(s.toString()),
child: Container(
width: 300,
height: 100,
),
))
.toList(),
onReorder: (int oldIndex, int newIndex) {
print("$oldIndex --- $newIndex");
}),
);
}
}
複製程式碼
- 首先我們定義好一組顏色的列表
- 然後在 build 方法中返回
ReorderableListView
ReorderableListView
中的 children 為用顏色定義好的Card
- 在
onReorder
回撥中列印兩個引數oldIndex
&newIndex
執行一下,看一下列印的 log:
可以看到確實是能列印出新舊兩個 index, 但是這裡有一個很明顯的問題,
我們大家都知道陣列的下標是從 0 開始,可以看到 第一次是 從 0 到 3,第二次是從 0 到 4,
但是講道理明明應該是 從 0 到 2,從 0 到 3。
那為什麼我前兩次移動後的 newIndex 都 +1 了呢?
我們這裡也不去深究,
既然我們要移動,那肯定也會對源資料進行操作,不然移動也都是假的。
所以,基於這樣的一個 newIndex,我們只需要這樣:
setState(() {
if(oldIndex < newIndex) {
newIndex -= 1;
}
var temp = _data.removeAt(oldIndex);
_data.insert(newIndex, temp);
});
複製程式碼
- 先判斷是向上還是向下拖拽
- 如果是向下拖拽,那麼 newIndex 會多加一個,我們把它減掉
- 然後我們刪除舊資料並儲存它
- 最後在新的 index 上插入
ListView 的拖拽排序和刪除
既然前面說到了 ListView
的刪除,那這裡也必須把它倆組合起來了:
其實程式碼非常簡單,當然這也得益於 Flutter 一切皆 Widget,我們只需要在 Card
上包裹一個 Dismissible
就ok了:
children: _data
.map((s) => Dismissible(
key: Key("dismiss $s"),
child: Card(
color: s,
key: Key(s.toString()),
child: Container(
width: 300,
height: 100,
),
),
))
.toList(),
複製程式碼
總結
在 Flutter 當中,我們可以封裝很多的 Widget 來為我們日後的開發來節省時間,
當然,也不要忘記 Flutter 當中的 Widget 測試