Flutter 實現下拉重新整理 非安卓原生效果
阿新 • • 發佈:2018-12-22
先附上連線
https://github.com/dikeboy/flutter-refrensh
這裡涉及到flutter中的 幾塊 動畫 ,事件點選, 非同步,
要自定義下拉重新整理 首先必須要了解Flutter 中的事件監聽方法
https://flutter.io/docs/development/ui/advanced/gestures
flutter 的手勢主要是兩個類listener 和GestureDecetor
Listener 主要類似touchevent 包括 按下,移動,鬆開 取消(劃出螢幕)
GestureDecetor 主要就是一些手勢形成效果 點選 雙擊 長按之類的
我這裡是寫在基類裡,因為呼叫到修改介面 當然你也可以改成Mixin
因為我這裡是對ListView進行處理 也就是通過修改ListView的第一個header高度來實現下拉效果
getRow(int position){ if(position==0){ return getRfrenshHeader(); } else{ return Text(list[position-1]); } }
這裡 -1 是因為本身header的高度已經算一個Item了
首先看下 state 的 initState方法
@override initState(){ super.initState(); animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this); //定義了一個300毫秒的動畫控制器 animation = Tween(begin: 1.0, end: 0.0).animate(animationController); //begin end 也就是300毫秒時間內aimation的值從1.0 到0.0變化animation.addListener(() { setState(() { // the animation object’s value is the changed state headHeight = (len-headNormalHeight) *animation.value+headNormalHeight; //從touchUp 釋放開始 header偏移量從1.0 到0.0移動 最終剩下header的高度 }); }); }
Listener的例項化
typedef MStartRefrensh = void Function(); //定義一個方法使用者回撥 類似介面回撥吧 getPullListener({Widget child, MStartRefrensh startRefrensh,double height}){ this.pointerUpListener = startRefrensh; headNormalHeight =height; //初始化Header的高度 if(scrollController==null){ scrollController = ScrollController(initialScrollOffset: height); //初始化滾動條 flutter中如果需要監聽 列表滑動 都需要滾動條 } Listener listener = new Listener( child: child, onPointerDown: pointDown,onPointerUp: pointUp,onPointerMove: pointMove,onPointerCancel: pointCancle,);//這裡也就是我們4個手勢的監聽方法 if(!firstJump){ setState(() { if(!firstJump&&scrollController.hasClients){ //hasClients可以用來判斷 scrollerController是否已經繫結到listview上 firstJump =true; scrollController.jumpTo(headNormalHeight); //第一次載入的時候 我們直接跳到Header的高度 從而讓header隱藏在螢幕上面 } }); } return listener; }
下面看下手勢事件
pointDown(PointerDownEvent event){ //沒沒啥特別 記錄下touchdown的座標 dx = event.position.dx; dy=event.position.dy; isTouchDown = true; } pointUp(PointerUpEvent event){ //釋放的時候 如果head大於我們初始化的head 就說明需要重新整理 啟動一個回滾動畫
if(headHeight>headNormalHeight){ startUpAnimation(headHeight); } else{ setState((){ headHeight=headNormalHeight; //這裡主要是重置 避免未知問題 }); } isTouchDown = false; } pointCancle(PointerCancelEvent event){ //cancel跟UP邏輯一樣 也可以自定義 if(headHeight>headNormalHeight){ startUpAnimation(headHeight); } else{ setState((){ headHeight=headNormalHeight; }); } isTouchDown = false; } pointMove(PointerMoveEvent event){ // print(_scrollController.position.pixels); setState(() { if(event.position.dy - dy>0) headHeight = (event.position.dy - dy)/2+headNormalHeight; //這裡我設定header高度是滑動距離的1/2 實際效果有些也會有越滑越慢 可以根據開根號 或者2次方實現 }); }
下面是動畫
startUpAnimation(double len) { this.len = len; if (animationController.isCompleted) { animationController.reset(); //動畫結束後重置 以便下次接著用 } animationController.forward().then((_) { headHeight = headNormalHeight; pointerUpListener(); //這個是自定義的回撥事件 也就是初始化的時候傳入的 // }); }
重新整理的header
//這裡就用了系統的滾動條 要重寫下來動畫啥的都可以修改這裡
getRfrenshHeader(){
if(headHeight==headNormalHeight&&!isTouchDown){
return Container(
child: SizedBox(
child: CircularProgressIndicator(valueColor: new AlwaysStoppedAnimation<Color>(Colors.orange)),
height: 20.0,
width: 20.0,
),
alignment: Alignment.center,
height: headHeight,
);
}
else{
return Container(
child: SizedBox(
child: Row(children: <Widget>[
new Image.asset('images/drop_refrensh.png'),
Text("釋放重新整理")
],
mainAxisAlignment: MainAxisAlignment.center,),
height: 20.0,
),
alignment: Alignment.center,
height: headHeight,
);
}
}
重新整理結束需要主動關閉
finishLoading(){ if(scrollController!=null&&scrollController.hasClients){ scrollController.animateTo( headNormalHeight, curve: Curves.easeOut, duration: const Duration(milliseconds: 100), ); } }