1. 程式人生 > >Flutter 實現下拉重新整理 非安卓原生效果

Flutter 實現下拉重新整理 非安卓原生效果

先附上連線

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),
       );
     }
  }