- 在Flutter开发中,页面的概念相较于原生来说被模糊了,Flutter没有页面类的概念,相对于的是一切皆Widget,不管一个页面还是一个组件,我们都称它为Widget,对于一些重复性基础的功能,我们希望可以像原生一样有一个base类来统一管理,基于实战项目,对基础页面的搭建进行下复盘整理。
基础页面需要的功能:1、生命周期管理。2、网络状态管理。3加载dialog统一管理。我们希望这些功能可以在基础页面进行统一管理。
1、基础StatefulWidget--> BaseStatefulPage 和 BaseStatefulPageState
- 1. 生命周期管理
基于原生生命周期封装。
代码:
abstract class BaseStatefulPage extends StatefulWidget {
const BaseStatefulPage({Key? key}) : super(key: key);
@override
BaseStatefulPageState<BaseStatefulPage> createState();
}
// AutomaticKeepAliveClientMixin 页面状态保持
// WidgetsBindingObserver 生命周期相关
abstract class BaseStatefulPageState<T extends BaseStatefulPage>
extends State<T>
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
/// 生命周期
/// [initState] widget创建执行 且只执行一次
/// [build] 状态更新就会执行
/// [didUpdateWidget] 页面重新build是调用
/// [onResumed] 界面可见 从后台切换到前台触发
/// [onPause] 应用不可见 切换到后台触发
/// [onInactive] 应用不可见或者弹出框覆盖本页面 一般不咋用
/// [dispose] 销毁widget 在此回调释放资源等
@override
void initState() {
super.initState();
// 注册
WidgetsBinding.instance?.addObserver(this);
}
@override
void dispose() {
super.dispose();
// 释放
WidgetsBinding.instance?.removeObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
LogUtil.i(state, tag: "state");
switch (state) {
case AppLifecycleState.resumed:
onResumed();
break;
case AppLifecycleState.inactive:
onInactive();
break;
case AppLifecycleState.paused:
onPause();
break;
case AppLifecycleState.detached:
break;
}
}
void onResumed() {}
void onInactive() {}
void onPause() {}
-
2. 网络状态管理
我希望在有网络请求的页面可以统一管理网络加载成功、失败等各种状态,首先我们需要一个加载基类我这边一共有:加载中、加载成功、加载失败、空数据四种状态,加载失败可以统一管理重新加载。代码:
/// 网络加载根布局
class LoadingLayout extends StatelessWidget {
/// 加载成功
static const int success = 0;
/// 加载中
static const int loading = 1;
/// 加载错误
static const int error = 2;
/// 数据为空
static const int empty = 3;
final Widget child; // 子布局
final Function? onReload; //重新加载方法
final int state; //页面标记
final ErrorRes? errorRes;
const LoadingLayout(
{Key? key,
this.onReload,
this.state = loading,
this.errorRes,
required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
// 点击其他部位关闭键盘
return KeyboardDismissOnTap(
child: Material(
color: Theme.of(context).backgroundColor,
child: Stack(
children: [child, _page(context)],
)));
}
Widget _page(BuildContext context) {
Widget widget;
switch (state) {
case loading:
widget = const LoadingPage();
break;
case success:
widget = const SizedBox();
break;
case empty:
widget = EmptyPage(onTab: onReload,);
break;
case error:
widget = ErrorPage(onReload, errorRes: errorRes);
break;
default:
widget = EmptyPage(onTab: onReload,);
break;
}
return widget;
}
}
在基础页面类里加入:
/// 构建具体业务页面
Widget buildPage(BuildContext context);
@override
Widget build(BuildContext context) {
super.build(context);
return LoadingLayout(
child: buildPage(context),
state: loadingState,
onReload: onReload,
errorRes: errorRes,
);
}
/// 初始化网络请求数据、 重新加载调用的方法
void initHttpData();
void onReload() {
setState(() {
loadingState = LoadingLayout.loading;
});
initHttpData();
}
/// state 请求状态
/// result 成功之后可以操作业务代码
setLoadingState(int state, {Function? result, ErrorRes? errorRes}) {
setState(() {
loadingState = state;
if (state == LoadingLayout.error) {
this.errorRes = errorRes;
}
if (result != null) result();
});
}
setLoading() {
setLoadingState(LoadingLayout.loading);
}
setSuccess({Function? result}) {
setLoadingState(LoadingLayout.success, result: result);
}
setError(ErrorRes res) {
setLoadingState(LoadingLayout.error, errorRes: res);
}
setEmpty() {
setLoadingState(LoadingLayout.empty);
}
- 3. 加载Dialog统一管理
///加载loading
showLoadingDialog(BuildContext context, {String? text}) {
showDialog(
context: context,
builder: (context) {
return LoadingDialog(
text: text,
);
});
}
/// 关闭loading
dismissLoadingDialog(BuildContext context) {
Navigator.pop(context);
}
具体的状态页面根据自己UI设计具体编写即可,这里只整理下实现的具体思路 --End