Android Flutter实现自定义下拉刷新组件

 更新时间:2022年08月24日 08:34:38   作者:JulyYu  
在Flutter开发中官方提供了多平台的下拉刷新组件供开发者使用。本文将改造一下这些组件,实现自定义的下拉刷新组件,感兴趣的可以了解一下

前言

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下拉刷新的资料请关注脚本之家其它相关文章!

相关文章

  • Android基于IJKPlayer视频播放器简单封装设计

    Android基于IJKPlayer视频播放器简单封装设计

    这篇文章主要介绍了Android基于IJKPlayer视频播放器简单封装设计,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • Android实现文字消除效果

    Android实现文字消除效果

    由于项目和语音识别相关,有时候人在不经意间交流的无效音频会被识别出来,并展示于界面,为了美观,客户要求我们将这些无效的识别文本用一个从右到左的动画给清除,于是便有了下述的技术实现。感兴趣的朋友可以参考下
    2021-06-06
  • Android中绝对音量和相对音量设置

    Android中绝对音量和相对音量设置

    大家好,本篇文章主要讲的是Android中绝对音量和相对音量设置,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • Android App中的多个LinearLayout嵌套布局实例解析

    Android App中的多个LinearLayout嵌套布局实例解析

    这篇文章主要介绍了Android App中的多个LinearLayout嵌套布局实例,利用线性布局来排列按钮是安卓应用布局中的常用做法,需要的朋友可以参考下
    2016-04-04
  • Android应用自动跳转到应用市场详情页面的方法

    Android应用自动跳转到应用市场详情页面的方法

    最近在工作中遇到一个需求,推广部门要求实现应用自动跳转到应用市场详情页面,通过查找一些资料,实现出来了,觉得有必要整理下方便以后或者有需要的朋友们参考借鉴,下面来一起详细看看Android应用自动跳转到应用市场详情页面的方法吧。
    2016-12-12
  • Android Http协议访问网络实例(3种)

    Android Http协议访问网络实例(3种)

    本篇文章主要介绍了Android Http协议访问网络实例(3种),具有一定的参考价值,有兴趣的可以了解一下
    2017-07-07
  • 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程

    这篇文章主要介绍了Android APP开发入门教程,从SDK下载、开发环境搭建、代码编写、APP打包等步骤一一讲解,非常简明的一个Android APP开发入门教程,需要的朋友可以参考下
    2014-05-05
  • Android绘图之Paint的使用方法详解

    Android绘图之Paint的使用方法详解

    这篇文章主要给大家介绍了关于Android绘图之Paint使用的相关资料,文中通过示例代码介绍的非常详细,并给大家介绍了DrawText 基线确定的方法,需要的朋友可以参考借鉴,下面随着小编来一些学习学习吧。
    2017-11-11
  • android通过google api获取天气信息示例

    android通过google api获取天气信息示例

    这篇文章主要介绍了android通过google api获取天气信息示例,需要的朋友可以参考下
    2014-04-04
  • Android开发之关于项目

    Android开发之关于项目

    本文是此系列文章的第二篇,给大家介绍的是项目相关的内容,非常的细致全面,有需要的小伙伴可以参考下
    2016-02-02

最新评论