Flutter Key的原理和使用(一) 没有Key会发生什么
Flutter Key的原理和使用(二) Widget 和 Element 的对应关系
Flutter Key的原理和使用(三) LocalKey的三种类型
Flutter Key的原理和使用(四) GlobalKey 的用法
Flutter Key的原理和使用(五) 需要key的实例:可拖动改变顺序的Listview
之前呢,我们介绍了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
.
之后有时间, 会实现一个支持拖动的列表组件,来更好的实现这个效果.