最近遇到个需求,要求在一个 ListView 里面能互换两个 item 的位置,这样:
于是,就有了现在的这个 WReorderList 。
WReorderList
功能就不用多说了,可以随意替换两个 item 的位置。
构造函数及其用法
还是按照老规矩,先来看一下构造函数:
WReorderList({ Key key, @required this.children, @required this.onIndexChanged, this.duration = const Duration(milliseconds: 500) }) : super(key: key);
一共四个参数:
1.key:不用多说 2.children:子组件集合 3.onIndexChanged:当两个位置变化之后的回调,要更改您自己的数据源 4.duration:动画时间
怎么用就很简单了:
WReorderList( key: key, children: children, onIndexChanged: (a, b) { setState(() { var temp = _colors[a]; _colors[a] = _colors[b]; _colors[b] = temp; }); }, ),
组件用上了, 那如何交换位置呢?
有两种方法:
1.给 WReorderList 设置一个 GlobalKey,然后 key.currentState.swap(0, 1) 就OK了 2.通过 WReorderList.of(context) 方法获取到 state,然后再调用 swap 方法就好了。
WReorderList 原理解析
分析原理
首先从技术角度分析一下:
1.怎么交换两个 item 位置? 2.如何获取到需要交换的两个 item 的组件 3.交换过程中两个 item 原来的位置上要被空白占用?
怎么交换两个 item 位置
这里我原本预想了好几种方案:
1.弹出一个Overlay,在 Overlay 上做动画 2.弹出一个 PopupRoute,用 Hero 动画 3.染出一个 PopupRoute,在上面做动画
第一种方案被否决了,因为我 Overlay 用的不是很多。
第二种我试了一下,发现 Hero 不能用,所以也否了。
那就只剩第三种了,我试了一下用 AnimatedPositioned ,发现是可以的,那就决定是他了。
如何获取到需要交换的两个 item 的组件
这个我原本也想过用 GlobalKey,但是在列表中有一大堆的 GlobalKey 又不好,
所以我定义了一个类,该类如下:
class WReorderData { Widget widget; BuildContext context; double height; WReorderData(this.widget); }
在用户传进来 children 以后,就用该类包住,获取到他的widget。
并且在 build 的时候用 Builder 包裹住就可以获取到当前这个 widget 的 context了。
就能获取到需要交换的两个 item 的位置。
交换过程中两个 item 原来的位置上要被空白占用?
可以看到最开始的效果中,交换过程中是被空白给占用了的,那这个高度如何获取?
我查了一下资料,发现这种是个有效的方法:
在 build 的时候加上一个 Future.delayed,100毫秒之后用当前 context 获取高度,这样就ok了。
代码如下:
Builder(builder: (context) { Future.delayed(Duration(milliseconds: 100), () { data[index].context = context; data[index].height = context.size.height; }); return swapIndex.contains(index) ? Container(height: data[index].height) : data[index].widget; });
总结
最近通过 PopupRoute 已经定义了两个组件了,感觉还是很有用的。
有个需要注意的点是:使用 WReorderList.of(context) 方法来获取 State 的话,一定要在子组件中,否则会找不到 state!!!
查看更多关于Flutter | WReorderList 一个可以指定两个item互换位置的组件的详细内容...