Flutter开发通用页面Loading组件示例详解

2022-11-28 11:09:02

目录总结前沿页面通用Loading组件是一个App必不可少的基础功能,之前只开发过Android原生的页面Loading,这次就按原生的逻辑再开发一个Flutter的Widget,对其进行封装复用我们...

目录
总结

前沿

页面通用Loading组件是一个App必不可少的基础功能,之前只开发过android原生的页面Loading,这次就按原生的逻辑再开发一个Flutter的Widget,对其进行封装复用

我们先看下效果: bloggif_60b603bed8875.gif

原理

状态

一个通用的页面加载Loading组件应该具备以下几种状态:

IDLE%20初始化

Idle状态,此时的组件还只是初始化

LOADING%20加载中

Loading状态,一般在网络请求或者耗时加载数据时调用,通用显示的是一个progress或者自定义的帧动画

LOADING_SUCCESS

LoadingSuccess加载成功,一般在网络请求成功后调用,并将需要展示的页面展示出来

LOADING_SUCCESS_BUT_EMPTY

页面加载成功但是没有数据,这种情况一般是发起列表数据请求但是没有数据,通常我们会展示一个空数据的页面来提醒用户

NETWORK_blockED

网络错误,一般是由于网络异常、网络请求连接超时导致。此时我们需要展示一个网络错误的页面,并且带有重试按钮,让用户重新发起请求

ERROR

通常是接口错误,这种情况下我们会根据接口返回的错误码或者错误文本提示用户,并且也有重试按钮

///%20状态枚举 enum%20LoadingStatus%20{ %20%20idle,%20//%20初始化 %20%20loading,%20//%20加载中 %20%20loading_suc,%20//%20加载成功 %20%20loading_suc_but_empty,%20//%20加载成功但是数据为空 %20%20network_blocked,%20//%20网络加载错误 %20%20error,%20//%20加载错误 } 复制代码

点击事件回调

当网络异常或者接口报错时,会显示错误页面,并且提供重试按钮,让用户点击重新请求。基于这个需求,我们还需要提供点击重试后的事件回调让业务可以处理重新请求。

%20///%20定义点击事件 %20typedef%20OnTapCallback%20=%20Function(LoadingView%20widget); 复制代码

提示文案

提供提示文案的自定义,方便业务根据自己的需求展示特定的提示文案

代码实现

根据上面的原理来实现对应的代码

构造方法
%20///%20构造方法 %20LoadingView({ %20%20%20%20Key%20key, %20%20%20%20@required%20this.child,%20//%20需要加载的Widget %20%20%20%20@required%20this.todoAfterError,%20//%20错误点击重试 %20%20%20%20@required%20this.todoAfterNetworkBlocked,%20//%20网络错误点击重试 %20%20%20%20this.networkBlockedDesc%20=%20"网络连接超时,请检查你的网络环境", %20%20%20%20this.errorDesc%20=%20"加载失败", %20%20%20%20this.loadingStatus%20=%20LoadingStatus.idle, %20})%20:%20super(key:%20key); 复制代码 根据不同的Loading状态展示对应的Widget
其中idle、success状态直接展示需要加载的Widget(这里也可以使用渐变动画进行切换过度)
%20///根据不同状态展示不同Widget %20%20Widget%20_buildBody()%20{ %20%20%20%20switch%20(widget.loadingStatus)%20{ %20%20%20%20%20%20case%20LoadingStatus.idle: %20%20%20%20%20%20%20%20return%20widget.child; %20%20%20%20%20%20case%20LoadingStatus.loading: %20%20%20%20%20%20%20%20return%20_buildLoadingView(); %20%20%20%20%20%20case%20LoadingStatus.loading_suc: %20%20%20%20%20%20%20%20return%20widget.child; %20%20%20%20%20%20case%20LoadingStatus.loading_suc_but_empty: %20%20%20%20%20%20%20%20return%20_buildLoadingSucButEmptyView(); %20%20%20%20%20%20case%20LoadingStatus.error: %20%20%20%20%20%20%20%20return%20_buildErrorView(); %20%20%20%20%20%20case%20LoadingStatus.network_blocked: %20%20%20%20%20%20%20%20return%20_buildNetworkBlockedView(); %20%20%20%20} %20%20%20%20return%20widget.child; %20%20} 复制代码 buildLoadingView,这里简单用了系统的CircularProgressIndicator,也可以自己显示帧动画
%20%20///%20加载中%20View %20%20Widget%20_buildLoadingView()%20{ %20%20%20%20return%20Container( %20%20%20%20%20%20width:%20double.maxFinite, %20%20%20%20%20%20height:%20double.maxFinite, %20%20%20%20%20%20child:%20Center( %20%20%20%20%20%20%20%20child:%20SizedBox( %20%20%20%20%20%20%20%20%20%20height:%2022.w, %20%20%20%20%20%20%20%20%20%20width:%2022.w, %20%20%20%20%20%20%20%20%20%20child:%20CircularProgressIndicator( %20%20%20%20%20%20%20%20%20%20%20%20strokeWidth:%202, %20%20%20%20%20%20%20%20%20%20%20%20valueColor:%20AlwaysStoppedAnimation<Color>(AppColors.primaryBgBlue), %20%20%20%20%20%20%20%20%20%20), %20%20%20%20%20%20%20%20), %20%20%20%20%20%20), %20%20%20%20); %20%20} 复制代码 其他提示页面,这里做了一个统一的封装
%20///%20编译通用页面 %20%20Container%20_buildGeneralTapView({ %20%20%20%20String%20url%20=%20"images/icon_network_blocked.png", String desc, @required Function onTap, }) { return Container( color: AppColors.primaryBgWhite, width: double.maxFinite, height: double.maxFinite, child: Center( child: SizedBox( height: 250.h, child: Column( children: [ Image.asset(url, width: 140.w, height: 99.h), SizedBox( height: 40.h, ), Text( desc, style: AppText.gray50Text12, maxLines: 2, overflow: TextOverflow.ellipsis, ), SizedBox( height: 30.h, ), if (onTap != null) BorderRedBtnWidget( content: "重新加载", onClick: onTap, padding: 40.w, ), ], ), ), ), ); } /// 加载成功但数据为空 View Widget _buildLoadingSucButEmptyView() { return _buildGeneralTapView( url: "images/icon_empty.png", desc: "暂无数据", onTap: null, ); } /// 网络加载错误页面 Widget _buildNetworkBlockedView() { return _buildGeneralTapView( url: "images/icon_network_blocked.png", desc: widget.networkBlockedDesc, onTap: () { widget.todoAfterNetworkBlocked(widget); }); } /// 加载错误页面 Widget _buildErrorView() { return _buildGeneralTapView( url: "images/icon_error.png", desc: widget.errorDesc, onTap: () { widget.todoAfterError(widget); }); } 复制代码

使用

  Widget _buildBody() {
     var loadingView = LoadingView(
      loadingStatus: LoadingStatus.loading,
      child: _buildContent(),
      todoAfterNetworkBlocked: (LoadingView widget) {
        // 网络错误,点击重试
        widget.updateStatus(LoadingStatus.loading);
        Future.delayed(Duration(milliseconds: 1000), () {
          widget.updateStatus(LoadingStatus.error);
        });
      },
      todoAfterError: (LoadingView widget) {
        // 接口错误,点击重试
        widget.updateStatus(LoadingStatus.loading);
        Future.delayed(Duration(milliseconds: 1000), () {
          // widget.updateStatus(LoadingStatus.loading_suc);
          widget.updateStatus(LoadingStatus.loading_suc_but_empty);
        });
      },
    );
     Future.delayed(Duration(milliseconds: 1000), (){
       loadingView.updateStatus(LoadingStatus.network_blocked);
     });
     return loadingView;
  }
复制代码

总结

至此已经完成了对整个Loading组件的封装,代码已上传github

以上就是Flutter开发通用页面Loading组件示例详解的详细内容,更多关于Flutter通用页面Loading组件的资料请关注我们其它相关文章!