Flutter加载(loading)页面 2023-01-20 周五

需求简介

一般的页面,大多数可以分为三种状态:

  1. 数据加载完成,正常显示;

  2. 数据加载完成,但是没有数据,一般会有一个空数据视图;
    加载后网络错误一般给个toast提示就好,给个错误视图并没有什么价值。

  3. 加载过程,一般是loading视图,这个就是当前的话题;

简单实现

一般的toast插件,都会提供一个loading视图。比如我们现在用的bot_toast插件,大多数时候用的是toast提示。网络访问时的loading视图用的也是他,而且直接做在底层的request接口。只要有网络访问,就自动显示。

企业微信截图_62fa2228-9bcf-473d-81e1-47bfbb65f5d3.png

自定义视图

如果简单的loading不喜欢,可以考虑自定义写一个。
这里有一个非常好用的插件lottie

他的特点是,先用AE工具做一个动画,然后借助工具Bodymovin转化为json文件。把这个json文件以资源的形式加入工程,然后就可以用这个lottie工具进行展示。

企业微信截图_efb3cd09-06a4-4605-a379-710af219e2fc.png

这样出来的loading动画比直接使用gif文件,质量上要好很多。所以这个插件还是很受欢迎的。

企业微信截图_41311865-2609-4049-9cee-5ef53829f304.png

使用

  • 把动画json文件以资源的形式加入工程。

  • 类似图片,用lottie工具加载。

企业微信截图_26bf171c-189f-49a3-866b-c7e8f51e9d87.png

封装

  • lottie工具实现的是动画以json资源文件的形式加载,并且画面质量很高。

  • 就像上面的视图,4个跳动的绿点是动画,但是灰色框,以及外面看不见的蒙层,那是需要自己写的。

loading视图

  Widget get _loadingView {
    // return Center(
    //   child: Lottie.network('https://assets3.lottiefiles.com/private_files/lf30_gvdwzaoj.json'),
    // );
    // return Center(child: Lottie.asset("assets/loading.json"));
    return Center(
      child: Container(
        color: Colors.transparent,
        height: ScreenUtil().screenHeight,
        width: ScreenUtil().screenWidth,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              decoration: BoxDecoration(
                color: const Color(0x99000000),
                borderRadius: BorderRadius.all(Radius.circular(5.w)),
              ),
              width: 100.w,
              height: 100.w,
              child: Center(
                child: SizedBox(
                  width: 60.w,
                  height: 60.w,
                  child: Lottie.asset("assets/lottie/loading.json"),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
  • Lottie加载的动画是60x60的跳动的绿点;

  • 绿点包在一个100x100的透明度60%黑色,带圆角的矩形中

  • 最外层是一个全屏尺寸的无色矩形,防止点击。

布局实现

这个loading视图做成了一个公共组件,主要的布局代码如下:

  @override
  Widget build(BuildContext context) {
    if (cover) {
      return Stack(
        children: [child, isLoading ? _loadingView : Container()],
      );
    } else {
      return isLoading ? _loadingView : child;
    }
  }
  • cover参数默认为true,就是loading视图以浮层的形式覆盖在原视图上(Stack组件实现)。为false的时候,就是只显示loading视图,不显示原视图。

  • 做成了公共组件,原视图以child的方式加入,然后使用者自己控制isLoading参数来达到loading视图的显示和隐藏。这个有点“套娃”的味道,和传统的继承感觉不大一样。

使用

  • 如果要全屏控制,可以把整个Scaffold都当做child传入;

  • 大多数时候,只要把body作为child的就可以了。(loading过程,可以点导航栏的返回按钮)

        body: LottieUtils(
          isLoading: logic.isShowLoading,
          cover: logic.hasData,
          child: buildBody(),
        ),
  • 使用两三个逻辑变量来控制loading视图的显示和隐藏
  /// 加载过程标记
  /// 是否有数据; 根据接口返回的total来判断
  /// 无数据,只显示loading,cover参数为false;
  /// 有数据,loading展示在原有视图之上,cover参数为true;
  bool get hasData {
    return total > 0;
  }

  bool isDataReady = false;
  bool get isShowLoading {
    return !isDataReady;
  }
  • 一般可以直接用在网络接口数据的访问中
  /// 获取预演包裹列表; 上拉,下拉,接口封装
  /// 加载过程标记做在这个接口上
  Future getRehearList({bool isRefresh = false}) async {
    /// 接口开始,显示loading
    isDataReady = false;
    update();

    StorageApi.previewItem(
      parameters: queryParams,
      success: (response) {
        /// 成功,隐藏loading
        isDataReady = true;
        update();
       
        /// 其他业务处理代码
          
      },
      fail: (e) {
        /// 失败,隐藏loading
        isDataReady = true;
        update();
      },
    );
  }

全局loading

在大多数情况下,上面的loading方案已经够用。但是,某些特殊情况,就力不从心。
上面的Loading和具体的页面深度绑定,有具体的上下文环境,可以工作很好。
对于那些离开具体上下文全局场景,需要那种浮层式的全局loading。

借用插件

全局浮层写起来麻烦,那么可以考虑借用插件来实现。工程中的toast用的是bot_toast,那么就可以借用这个来实现。

  • 既然是全局的loading,那么就直接做成静态函数。
  static showToastLoading() {
    BotToast.showCustomLoading(
      toastBuilder: (cancelFunc) {
        return _loadingView;
      },
    );
  }

  static hideToastLoading() {
    BotToast.closeAllLoading();
  }
  • 具体的 _loadingView,就是上面用到的
  static Widget get _loadingView {
    return Center(
      child: Container(
        color: Colors.transparent,
        height: ScreenUtil().screenHeight,
        width: ScreenUtil().screenWidth,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              decoration: BoxDecoration(
                color: const Color(0x99000000),
                borderRadius: BorderRadius.all(Radius.circular(5.w)),
              ),
              width: 100.w,
              height: 100.w,
              child: Center(
                child: SizedBox(
                  width: 60.w,
                  height: 60.w,
                  child: Lottie.asset("assets/lottie/loading.json"),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
  • 使用起来和BotToast一样简单,只要保证LottieUtils.showToastLoading()和LottieUtils.hideToastLoading()成对出现就可以了,不需要考虑具体的布局。反正一律是全屏的浮层。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容