美文网首页Flutter教程网
FLUTTER 自己动手对LISTVIEW添加下拉刷新

FLUTTER 自己动手对LISTVIEW添加下拉刷新

作者: 写一点是一点 | 来源:发表于2019-07-17 23:06 被阅读2次

    material实际上自带有下拉刷新控件,不过既然是学习就自己实现一个,这里是实现一个IOS中比较常见的tableviewHeader式的下拉刷新控件 。效果类似于MJNormalHeader。


    MJHEADER

    先踩个坑

    在做下拉刷新的时候,我首先是利用滚动控件拉出滚动范围时,利用offset做处理。 也实现了,在IOS端listview有弹簧效果,因此会被拉出负的offset,但是在安卓端运行时弹簧效果却会被系统的下拉特效覆盖掉 。
    因此在IOS端运行很好的下拉事件却在安卓端无法完成功能。


    安卓下拉
    Ios下拉

    接下来实现下拉刷新

    既然无法使用弹簧效果来实现,那么换一个思路,在listview的头部添加一个padding占位。这样就只需要改动padding的高度就可以实现将listview拉出滚动范围的效果了。

    1. 先用自定义statefulWidget包装一下listview,并在顶部合适的位置放一个指示下拉刷新的text,设置两个属性分别用来给内部的listview传递元素数量和build,封装一个方法,用以调起刷新事件,并将结束刷新的事件传递出去
    typedef headerRefersh = void Function(Function stopRef);
    class RefershScroller extends StatefulWidget{
    
     IndexedWidgetBuilder builder;
    
     int count;
    
    bool refershing = false;
    //下拉刷新回调
    
    headerRefersh ref;
    @override
    RefershScroller({Key key,this.builder,this.ref,this.count});
    .......
    
    1. 界面布局 用一个属性记录一下顶部的偏移量作为下拉的效果使用,并用Listener用来监听触摸事件。
    class _RefershScrollerState extends State<RefershScroller>{
    //滚动控制器
    ScrollController controller = new ScrollController(keepScrollOffset: false,initialScrollOffset: 0,);
    //刷新标记
    String loadingStr = '最后刷新时间:';
    String time ='--:--';
    //顶部偏移量
    double topMargin = 0;
    //开始下拉刷新的标记
    bool onTop = false;
    ......
    @override
    Widget build(BuildContext context) {
      // TODO: implement build
      return new Stack(
          children: <Widget>[
        //下拉控件的提示信息
            Positioned(top: 20,child:Text(loadingStr + time,style: TextStyle(color: SkinConfig().titleTextColor()),textAlign: TextAlign.center,),),
    
            Container(padding: EdgeInsets.fromLTRB(0, topMargin, 0, 0),
            //顶部间隔
            //包裹触摸事件监听
                     child:Listener(
              
                       onPointerUp:onPointUp ,
                       onPointerMove: onPointMove,
                       child:  new ListView.builder(
                         itemCount: widget.count,
                         itemBuilder: widget.builder,
                         controller: controller,
                       )
                     ),
                 )
          ],
        alignment: Alignment.center,
        );
    }
    

    完成业务逻辑:

    1. 利用滚动控制器,我们可以监听到listview滚动到顶部的事件,在这个事件中,记录下控件到达顶部这一状态。
    @override
      void initState() {
        // TODO: implement initState
    
      controller..addListener((){
        if(controller.offset == 0){
          onTop = true;
        }
        if(controller.offset > 0){
          onTop = false;
        }
      });
       super.initState();
      }
    
    1. 利用触摸事件获取手指在Y轴方向的移动数据delta.dy,在到达顶部这一状态为true的时候将delta.dy加到padding的高度上。
    //触摸移动事件
      void onPointMove(PointerMoveEvent event){
      //listview到达顶部时记录标记
      if(controller.offset == 0){
        this.onTop = true;
      }
      
      if(onTop){
      //到达顶部且继续下拉时将下拉的高度加到padding并更新状态
     
      if(this.topMargin + event.delta.dy > 0){
      
       this.topMargin = this.topMargin +  event.delta.dy;
       
      }
      setState(() {
    
      });
    
      }
      }
    
    1. 在触摸结束事件中开始刷新事件
    void onPointUp(PointerUpEvent event){
    
    //判断拉到一定高度
      if(this.topMargin > 60 ){
    //判断当前是否已经在刷新状态
        if( widget.refershing == false){
    
           widget.refershing = true;
           //回调刷新事件并将停止刷新的方法传递出去
           widget.ref(stopRefersh);
      //  让控件保持在刷新状态的高度
          controller.jumpTo(60 - this.topMargin);
          this.topMargin = 60;
           setState(() {
        });
    
        }
      }else if(this.topMargin > 0){
      //如果没有拉到足够高度则回到原位
        double current = this.topMargin;
        this.topMargin = 0;
        controller.jumpTo(-current);
           setState(() {
        });
      }
    }
    

    你可能注意到了 在这里我是先让listview跳到一个-的offset , controller.jumpTo(60 - this.topMargin);
    再让顶部保持高度
    this.topMargin = 60;
    这么做的目的是平滑动画
    实际上android中也是有拉出边缘的弹簧效果的 这个效果由
    listview的shrinkWrap这个字段控制.它的默认值是false,如果将这个字设置为true则不管在哪个平台就都没有弹簧效果了
    因此虽然拉出范围在android中被屏蔽了,仍然可以利用jumpTo跳到一个-的offset,控件就会自然的"收缩"回到offset=0的位置。这样只要算一下当前位置和需要跳到位置的差值jump过去,listview就会自己平滑的滚动到目标位置了。

    1. 外部的回调
    void stopRefersh(){
      widget.refershing = false;
      setState(() {
    
        time = TimeOfDay.now().hour.toString() + ":" + TimeOfDay.now().minute.toString();
        this.topMargin = 0;
        controller.jumpTo(-60);
    
      });
    }
    

    同上 先把顶部的间隔归零 然后jumpTo(-60);listview就会自然地回到顶部了


    在这里插入图片描述

    对比一下两边的效果

    看看使用

    在这里插入图片描述

    自己试试吧!

    相关文章

      网友评论

        本文标题:FLUTTER 自己动手对LISTVIEW添加下拉刷新

        本文链接:https://www.haomeiwen.com/subject/kuyhlctx.html