文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

AndroidFlutter实现自定义下拉刷新组件

2024-04-02 19:55

关注

前言

Flutter开发中官方提供了多平台的下拉刷新组件供开发者使用,例如RefreshIndicatorCupertinoSliverRefreshControl分别适配AndroidiOS下拉刷新交互形态。但实际情况中这两者使用情况却不太相同在使用场景就存在差异,RefreshIndicator作为嵌套型下拉组件列表内容作为它的child使用而CupertinoSliverRefreshControl是嵌入在Sliver列表中使用。同时对于交互设计来说一般更偏好RefreshIndicator下拉形式,通过下拉列表整体下移后透出拉下刷新组件样式。

改造点

DIY下拉组件样式

RefreshIndicator下拉组件样式可能会在交互上不符合设计师要求。例如下拉过程中loading样式出现交互是会和列表重合,实际需求可能是希望下拉过程中loading样式和列表一样同步下移出现。

因此修改原有的下拉刷新组件样式构建,构建方法入参主要是refreshState、pulledExtent、refreshTriggerPullDistance、refreshIndicatorExtent。原逻辑中组件偏量是固定不变_kActivityIndicatorMargin值,因此下拉组件样式是直接显示出来的。

调整方案根据pulledExtent下拉距离,默认偏移下拉组件样式自身高度加上下拉距离从而将偏移量从负方向向正方向展示。

 Widget buildRefreshIndicator(
      BuildContext context,
      RefreshIndicatorMode refreshState, //下拉状态
      double pulledExtent, // 下拉实时距离
      double refreshTriggerPullDistance, // 下拉限制最大高度
      double refreshIndicatorExtent, // 下拉组件最大高度
      ) {
    return Container(
    color: Colors.deepOrange,
    child: Stack(
      clipBehavior: Clip.none,
      children: <Widget>[
        Positioned(
          top: -refreshIndicatorExtent + pulledExtent,
          left: 0.0,
          right: 0.0,
          //简易的下拉样式 忽略refreshState状态
          child: Container(
            child: Text("我是下拉呀~~~~",style: TextStyle(color: Colors.white,fontSize: 20,),textAlign: TextAlign.center,),
          ),
        ),
      ],
    ),
  );
  }

刷新时机调整

RefreshIndicator下拉组件另外刷新触发交互点也不是设计交互期望的逻辑,它的刷新触发机制是只要下拉超过设置下拉距离并会触发。但实际开发中可能并不希望当到达对应点就去做刷新操作而是下拉到一定距离松手后才会触发,因此需要改造下拉刷新组件内部的刷新机制。

原下拉刷新逻辑如下关键代码所示,只要当RefreshIndicatorMode.drag状态下并且latestIndicatorBoxExtent > widget.refreshTriggerPullDistance时就会触发下拉刷新方法。

  RefreshIndicatorMode transitionNextState() {
    RefreshIndicatorMode nextState;
。、、、 、、、 省略
      drag:
      case RefreshIndicatorMode.drag:
        if (latestIndicatorBoxExtent == 0) {
          return RefreshIndicatorMode.inactive;
        } else if (latestIndicatorBoxExtent < widget.refreshTriggerPullDistance) {
          return RefreshIndicatorMode.drag;
        } else {
          // 当latestIndicatorBoxExtent > widget.refreshTriggerPullDistance就执行
          if (widget.onRefresh != null) { //刷新逻辑执行点
            HapticFeedback.mediumImpact();
            SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
              refreshTask = widget.onRefresh()..whenComplete(() {
                if (mounted) {
                  setState(() => refreshTask = null);
                  refreshState = transitionNextState();
                }
              });
              setState(() => hasSliverLayoutExtent = true);
            });
          }
          return RefreshIndicatorMode.armed;
        }
        break;
      case RefreshIndicatorMode.armed:
        if (refreshState == RefreshIndicatorMode.armed && refreshTask == null) {
          goToDone();
          continue done;
        }

        if (latestIndicatorBoxExtent > widget.refreshIndicatorExtent) {
          return RefreshIndicatorMode.armed;
        } else {
          nextState = RefreshIndicatorMode.refresh;
        }
        continue refresh;
      refresh:
      case RefreshIndicatorMode.refresh:
        if (refreshTask != null) {
          return RefreshIndicatorMode.refresh;
        } else {
          goToDone();
        }
        continue done;
      	、、、、、省略
    }

    return nextState;
  }

弱希望下拉松手后判断是否触发刷新只修改RefreshIndicator下拉组件似乎无法直接满足条件。因此需要结合手势监听来完成,需要对整体框架代码做一个调整。

增加Listener嵌套监听手势抬起操作,获取MagicSliverRefreshControlState(原是私有类放开为公有)判断是否是超出下拉最小刷新间距,对内部是否可刷新标记进行赋值操作。

GlobalKey<MagicSliverRefreshControlState> key = GlobalKey<MagicSliverRefreshControlState>();

Listener(
      child: CustomScrollView(
        physics: BouncingScrollPhysics(),
        slivers: <Widget>[
          MagicSliverRefreshControl(
            key: key,
            builder: buildRefreshIndicator,
            onRefresh: () async {
              print("<> SliverRefreshControl onRefresh start");
              await Future.delayed(Duration(seconds: 2),(){});
              print("<> SliverRefreshControl onRefresh end");
            },
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
                  (content, index) {
                return Common.getWidget(index);
              },
              childCount: 100,
            ),
          )
        ],
      ),
      onPointerUp: (event){ //判断是否可刷新操作
        if(key?.currentState?.isCanRefreshAction() ?? false){
          key?.currentState?.canRefresh = true;
        }else{
          key?.currentState?.canRefresh = false;
        }
      },
    );

RefreshIndicator组件内部增加一种新状态RefreshIndicatorMode.over用来判断是否刷新临界状态,结合外部手势抬手监听。当下拉超出刷新最小间距且抬手放开判断触发刷新操作,over恢复到drag还是进入armed都是通过以上条件来实现的,其他原逻辑保持不变。

switch (refreshState) {
      case RefreshIndicatorMode.inactive:
        if (latestIndicatorBoxExtent <= 0) {
          return RefreshIndicatorMode.inactive;
        } else {
          nextState = RefreshIndicatorMode.drag;
        }
        continue drag;
      drag:
      case RefreshIndicatorMode.drag:
        if (latestIndicatorBoxExtent == 0) {
          return RefreshIndicatorMode.inactive;
        }
        else if (latestIndicatorBoxExtent < widget.refreshTriggerPullDistance) {
          return RefreshIndicatorMode.drag;
        } else {
          return RefreshIndicatorMode.over; //增加一种状态 表示下拉满足刷新条件
        }
        break;
    	/// 进入新状态后结合抬手后是否可刷新标记为判断是进入刷新方法还是回到拖拽状态
      case RefreshIndicatorMode.over:
        if (latestIndicatorBoxExtent <= widget.refreshTriggerPullDistance) {
          if(canRefresh){
            canRefresh = false; //将刷新标记置空复位
            if (widget.onRefresh != null) {
              HapticFeedback.mediumImpact();
              SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
                refreshTask = widget.onRefresh()..whenComplete(() {
                  if (mounted) {
                    setState(() => refreshTask = null);
                    refreshState = transitionNextState();
                  }
                });
                setState(() => hasSliverLayoutExtent = true);
              });
            }
            return RefreshIndicatorMode.armed;
          }else{
            return RefreshIndicatorMode.drag;
          }
        }
        return RefreshIndicatorMode.over;
        break;

效果展示

具体代码看这里

调整前

调整后

以上就是Android Flutter实现自定义下拉刷新组件的详细内容,更多关于Android Flutter下拉刷新的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯