這是我參與8月更文挑戰的31天,活動詳情檢視:8月更文挑戰
Flutter Key的原理和使用 (一) 沒有Key會發生什麼 (juejin.cn)
Flutter Key的原理和使用 (二) Widget 和 Element 的對應關係 (juejin.cn)
Flutter Key的原理和使用(三) LocalKey的三種型別 (juejin.cn)
Flutter Key的原理和使用(四) GlobalKey 的用法 - 掘金 (juejin.cn)
Flutter Key的原理和使用(五) 需要key的例項:可拖動改變順序的Listview - 掘金 (juejin.cn)
之前呢,我們介紹了flutter中的幾種key,它們相應的原理和使用方式, 這次就來複習一下,看看一個需要用到key的例項.
ReorderableListView
你可能使用過ListView
元件,它可以調整,顯示和滾動專案列表. 但是不能做的一件事就是在列表中移動專案.幸運的是,有ReorderableListView
.
ReorderableListView
是不個不常用的ListView元件.它是一個使用者可以通過拖動來重新排序其專案的列表元件
. 有了它,我們可以通過長按專案來在ListView的滾動方向移動它,將其放在新的位置.
它有一個要求: 所有列表項都必須有一個Key
並且必須實現onReorder
方法,它是使用者在重新排序列表時呼叫的回撥方法,看一下沒有實現這個回撥的效果:
ReorderableListView(
children: [
Box(Colors.blue, key: ValueKey(1)),
Box(Colors.green, key: ValueKey(2)),
Box(Colors.red, key: _globalKey),
],
onReorder: (oldIndex, newIndex) {
print('從位置$oldIndex移動到$newIndex');
},
)
複製程式碼
可以看到雖然我們交換了紅綠兩個box,但是鬆手之後UI並沒有變化,就是因為沒有沒有實現onReorder
.
onReorder: (oldIndex, newIndex)
, 分別有兩個引數,oldIndex和newIndex, 它們代表的含義就是,拖動的widget拖動前和拖動後的index. 看一下列印結果:
從位置2移動到1
說明是從第2個位置移動到了第1個位置(最開始是第0的位置). 每當發生這樣改變的時候,我們就需要把list元件修改. 修改一下我們的程式碼,移除舊位置的widget,在新的位置插入它:
final boxList = [
Box(Colors.blue, key: ValueKey(1)),
Box(Colors.green, key: ValueKey(2)),
Box(Colors.red, key: ValueKey(3)),
];
Widget listWidget() {
return ReorderableListView(
children: boxList,
onReorder: (oldIndex, newIndex) {
print('從位置$oldIndex移動到$newIndex');
final box = boxList.removeAt(oldIndex);
boxList.insert(newIndex, box);
},
);
}
複製程式碼
這樣就達到了我們移動widget的目的. 但其實將widget從上向下移動的時候,有一個問題,我再移動一下大家看一下:
相應的列印: 從位置0移動到2. 簡單看一下原因:
index | widget |
---|---|
0 | box1 |
1 | box2 |
2 | box2 |
我們剛才移動的順序是將box1移動到box2的後面 , 體現在這裡就是從0移動到box2後面,也就是說是移動到了2的位置,因為box2的位置是1嘛. 不知道大家有沒有理解. 當然了,如果從起始位置移動到最後,就會出現陣列越界的報錯:
════════ Exception caught by animation library ═════════════════════════════════════════════════════
The following RangeError was thrown while notifying status listeners for AnimationController:
Invalid value: Not in inclusive range 0..2: 3
When the exception was thrown, this was the stack:
#0 List.insert (dart:core-patch/growable_array.dart:11:7)
#1 _MyHomePageState.listWidget.<anonymous closure> (package:flutter_key/home_page.dart:53:17)
#2 SliverReorderableListState._dropCompleted (package:flutter/src/widgets/reorderable_list.dart:646:24)
#3 _DragInfo._dropCompleted (package:flutter/src/widgets/reorderable_list.dart:1163:22)
#4 _DragInfo.startDrag.<anonymous closure> (package:flutter/src/widgets/reorderable_list.dart:1134:9)
...
The AnimationController notifying status listeners was: AnimationController#aca1a(⏮ 0.000; paused; DISPOSED)
════════════════════════════════════════════════════════════════════════════════════════════════════
複製程式碼
所以在向下移動的時候,我們要額外處理一下,新的index要減1,之後再進行刪除和插入操作:
if(newIndex>oldIndex){
newIndex --;
}
複製程式碼
橫向列表,就是向右的操作要處理
ReorderableListView的缺點
在拖了幾下之後,發現了幾個缺點:
- 長按才能觸發拖動,容易誤觸.
- ReorderableListView仍然是一個Listview,就是說它是會滾動的,當列表很長可以滾動的時候,會有很多誤操作.
- 一維的ListView,我們都知道listview只能在滾動方向來滑動,ReorderableListView也是一樣,不能上下左右來回拖動.
其實我們可以通過自己來實現一個這樣的元件.
拖動的話,我們可以通過Draggable
來實現,它是一個支援拖拽的widget.
為了避免滾動.可以使用Column
和Row
.
之後有時間, 會實現一個支援拖動的列表元件,來更好的實現這個效果.