注册

Flutter通用页面Loading组件

前沿


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


我们先看下效果:
bloggif_60b603bed8875.gif


原理


状态


一个通用的页面加载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);

提示文案


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


代码实现


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



  1. 构造方法

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


  1. 根据不同的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;
}


  1. 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),
),
),
),
);
}


  1. 其他提示页面,这里做了一个统一的封装


/// 编译通用页面
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;
}

总结


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


作者:Fitem
链接:https://juejin.cn/post/7063025458761170981
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册