Flutter开发通用页面Loading组件示例详解
作者:Fitem 发布时间:2022-05-18 23:41:09
前沿
页面通用Loading组件是一个App必不可少的基础功能,之前只开发过Android原生的页面Loading,这次就按原生的逻辑再开发一个Flutter的Widget,对其进行封装复用
我们先看下效果:
原理
状态
一个通用的页面加载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


猜你喜欢
- 工作中因业务需求,将数据库中的树状结构的数据根据父节点获取所有的子节点实现思路1.获取整个数据的list集合数据2.将数据分组,java8
- 本文实例为大家分享了android实现文件读写的具体代码,供大家参考,具体内容如下读取/*** 文件读取* @param is 文件的输入流
- 场景:NFC是目前Android手机一个主流的配置硬件项,本文主要讲解一下Android开发中,NFC刷卡的两种实现方式以及相关方法源码解析
- 环境操作系统windows10JDKjdk1.8.0_192IDEEclipse IDE for Enterprise Java Devel
- 基于Java的简单的用户管理系统,供大家参考,具体内容如下此系统功能和方法都比较简单本次系统通过控制台输入商品的基本信息,加入管理员的登录与
- 一、Autowired注解的用法1、概述使用spring开发时,进行配置主要有两种方式,一是xml的方式,二是java config的方式。
- 今天在群里看见有人问了这个问题,那就把我自己总结的知识拿出来与大家分享一下吧..当然可能还有什么不对的地方,希望指出:***msbase.j
- MyBatis Plus插件MyBatis Plus提供了分页插件PaginationInterceptor、执行分析插件SqlExplai
- 本文实例为大家分享了Android实现中国象棋游戏的具体代码,供大家参考,具体内容如下实现环境: android studio
- 本文主要探究的是关于Bean的作用域、生命周期的相关内容,具体如下。Bean的作用域Spring 3中为Bean定义了5中作用域,分别为si
- 前言说到 ADB 大家应该都不陌生,即 Android Debug Bridge,Android调试桥,身为 Android 开发的我们,熟
- struct OutputFilestruct OutputFile 是单个输出文件的管理器。之前在 parse_opt
- springcloud整合stream,rabbitmq实现消息驱动功能1.代码实现:创建项目stream添加依赖<parent>
- 面试题1:说一下你对ReentrantLock的理解?ReentrantLock是JDK1.5引入的,它拥有与synchronized相同的
- 需求描述一个五子棋游戏,能实现双方黑白对决,当一方获胜时给出提示信息,利用GUI界面实现项目结构如下图一、实体FiveChess类提供五子棋
- 以下共有4个函数分别是:1.从剪切板获得文字。2.将字符串复制到剪切板。3.从剪切板获得图片。4.复制图片到剪切板。/** * 从剪切板获得
- 首先添加一个timer,50susing System;using System.Collections.Generic;using Sys
- 本文实例为大家分享了Android实现点击获取验证码60秒后重新获取的具体代码,供大家参考,具体内容如下上代码/** * Created b
- 像ipconfig /all 这样的CMD命令想必大家都知道,但是很多童鞋可能不知道怎么写这样的控制台带参数的程序,其实很简单,我们先看建立
- 1.实现如图所示的单选效果由于Android提供的单选按钮radiobutton只能单行或单列显示,且样式并不美观,故可用GridView进