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

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

先踩个坑

在做下拉刷新的时候,我首先是利用滚动控件拉出滚动范围时,利用offset做处理。 也实现了,在IOS端listview有弹簧效果,因此会被拉出负的offset,但是在安卓端运行时弹簧效果却会被系统的下拉特效覆盖掉 。
因此在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就会自然地回到顶部了
在这里插入图片描述
对比一下两边的效果

看看使用

在这里插入图片描述
自己试试吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值