最近遇到個需求,要求在一個 ListView 裡面能互換兩個 item 的位置,這樣:
於是,就有了現在的這個 WReorderList
。
WReorderList
功能就不用多說了,可以隨意替換兩個 item 的位置。
建構函式及其用法
還是按照老規矩,先來看一下建構函式:
WReorderList({
Key key,
@required this.children,
@required this.onIndexChanged,
this.duration = const Duration(milliseconds: 500)
}) : super(key: key);
複製程式碼
一共四個引數:
- key:不用多說
- children:子元件集合
- onIndexChanged:當兩個位置變化之後的回撥,要更改您自己的資料來源
- duration:動畫時間
怎麼用就很簡單了:
WReorderList(
key: key,
children: children,
onIndexChanged: (a, b) {
setState(() {
var temp = _colors[a];
_colors[a] = _colors[b];
_colors[b] = temp;
});
},
),
複製程式碼
元件用上了, 那如何交換位置呢?
有兩種方法:
- 給
WReorderList
設定一個 GlobalKey,然後key.currentState.swap(0, 1)
就OK了 - 通過
WReorderList.of(context)
方法獲取到 state,然後再呼叫 swap 方法就好了。
WReorderList 原理解析
分析原理
首先從技術角度分析一下:
- 怎麼交換兩個 item 位置?
- 如何獲取到需要交換的兩個 item 的元件
- 交換過程中兩個 item 原來的位置上要被空白佔用?
怎麼交換兩個 item 位置
這裡我原本預想了好幾種方案:
- 彈出一個Overlay,在 Overlay 上做動畫
- 彈出一個 PopupRoute,用 Hero 動畫
- 染出一個 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!!!
程式碼已上傳至 GitHub:github.com/fluttercand…
並且釋出控制元件到了 Pub:pub.dev/packages/w_…
另我個人建立了一個「Flutter 交流群」,可以新增我個人微信 「17610912320」來入群。
推薦閱讀: