软件编程
位置:首页>> 软件编程>> Android编程>> Flutter开发通用页面Loading组件示例详解

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

作者:Fitem  发布时间:2022-05-18 23:41:09 

标签:Flutter,通用页面,Loading,组件

前沿

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

我们先看下效果:

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

原理

状态

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

IDLE 初始化

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

LOADING 加载中

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

LOADING_SUCCESS

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

LOADING_SUCCESS_BUT_EMPTY

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

NETWORK_BLOCKED

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

ERROR

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

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

点击事件回调

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

/// 定义点击事件
typedef OnTapCallback = Function(LoadingView widget);

提示文案

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

代码实现

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

  • 构造方法

/// 构造方法
LoadingView({
   Key key,
   @required this.child, // 需要加载的Widget
   @required this.todoAfterError, // 错误点击重试
   @required this.todoAfterNetworkBlocked, // 网络错误点击重试
   this.networkBlockedDesc = "网络连接超时,请检查你的网络环境",
   this.errorDesc = "加载失败",
   this.loadingStatus = LoadingStatus.idle,
}) : super(key: key);
  • 根据不同的Loading状态展示对应的Widget

  • 其中idle、success状态直接展示需要加载的Widget(这里也可以使用渐变动画进行切换过度)

///根据不同状态展示不同Widget
 Widget _buildBody() {
   switch (widget.loadingStatus) {
     case LoadingStatus.idle:
       return widget.child;
     case LoadingStatus.loading:
       return _buildLoadingView();
     case LoadingStatus.loading_suc:
       return widget.child;
     case LoadingStatus.loading_suc_but_empty:
       return _buildLoadingSucButEmptyView();
     case LoadingStatus.error:
       return _buildErrorView();
     case LoadingStatus.network_blocked:
       return _buildNetworkBlockedView();
   }
   return widget.child;
 }
  • buildLoadingView,这里简单用了系统的CircularProgressIndicator,也可以自己显示帧动画

/// 加载中 View
 Widget _buildLoadingView() {
   return Container(
     width: double.maxFinite,
     height: double.maxFinite,
     child: Center(
       child: SizedBox(
         height: 22.w,
         width: 22.w,
         child: CircularProgressIndicator(
           strokeWidth: 2,
           valueColor: AlwaysStoppedAnimation<Color>(AppColors.primaryBgBlue),
         ),
       ),
     ),
   );
 }
  • 其他提示页面,这里做了一个统一的封装

/// 编译通用页面
 Container _buildGeneralTapView({
   String url = "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;
 }

来源:https://juejin.cn/post/7063025458761170981

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com