一、前言:
Flutter中几乎所有的对象都是一个Widget,StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也是Flutter中非常重要的两个抽象类。我们做Flutter开发通常使用StatelessWidget和StatefulWidget,为了开发方便快捷,我们会封装Widget基类。封装基类我们首先要了解Widget生命周期,由于 StatelessWidget 的生命周期比较简单,它只有一个 build 方法,我们先不考虑封装,而StatefullWidget 的生命周期比较复杂,我们需要封装StatefullWidget,这样要使用StatefullWidget时我们直接继承基类。
二、生命周期
initState:插入渲染树时调用,只调用一次
didChangeDependencies:state依赖的对象发生变化时调用
didUpdateWidget:组件状态改变时候调用,可能会调用多次
build:构建Widget时调用
deactivate:当移除渲染树的时候调用
dispose:组件即将销毁时调用
Flutter组件State的生命周期整理如下图所示:
三、基类封装与使用
封装代码如下:
abstract class BasePage extends StatefulWidget {
final Key key;
BasePage({this.key}):super(key:key);
@override
State<StatefulWidget> createState() => cState();
State<StatefulWidget> cState();
}
abstract class BaseState<T extends BasePage> extends State<T> {
String _appTitle = 'Title';
List<Widget> _actions;
Color bgColor = Color(0xFFF6F6F6);
BuildContext mContext;
@override
void initState() {
beforeInit();
super.initState();
onCreate();
}
@override
Widget build(BuildContext context) {
mContext = context;
return Scaffold(
appBar: appbar(),
body: pageBody(context),
backgroundColor: bgColor,
floatingActionButton: floBtn(),
);
}
@override
void dispose() {
beforeDispose();
super.dispose();
onDestroy();
}
//页面初始化
void onCreate();
//页面布局
Widget pageBody(BuildContext context);
//页面销毁
void onDestroy();
//初始化之前的操作
void beforeInit() {
}
//销毁之前的操作
void beforeDispose() {
}
/*
销毁页面
*/
void finish(){
APPNavigator.pop(mContext);
}
/*
* 公用的AppBar的title
*/
void setAppTitle(String title, {List<Widget> actions}){
_appTitle = title;
_actions = actions;
}
/*
* 公用的AppBar
*/
Widget appbar(){
return AppBar(
title: Text(_appTitle, style: TextStyle(color: Colors.white, fontSize: 16),),
centerTitle: true,
leading: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Icon(Icons.arrow_back, color: Colors.white,),
onTap: (){
finish();
},
),
actions: _actions,
);
}
/*
* 悬浮按钮
*/
Widget floBtn(){
return null;
}
}
使用如下:
class SplashPage extends BasePage{
@override
State<StatefulWidget> cState() => _SplashPage();
}
class _SplashPage extends BaseState<SplashPage>{
@override
void onCreate() {
//setAppTitle('Splash'); // 使用通用的appBar
}
@override
Widget pageBody(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Color.fromRGBO(0, 0, 0, 0),
child: Icon(Icons.add_shopping_cart),
);
}
@override
void onDestroy() {
// TODO: implement onDestroy
}
Widget appbar(){
return null;// 去掉appbar
}
}
四、说明
我们重点关注了initState、build、dispose三个生命周期,其他生命周期也可自行封装,这里是最简洁、仅针对核心生命周期的封装。我们还可以拓展,将LoadingDialog、DefaultTextStyle、屏幕适配、Toast等封装在基类。
例如LoadingDialog,代码如下:
abstract class BaseState<T extends BasePage> extends State<T> {
BuildContext _loadingContext;
......省略
/*
* 显示 公用的对话框-加载中...
* backPress:点击返回,true消失,false不消失
* cancelOutside: 点击加载框外区域,true消失, false不消失
*/
void showLoading({String str, bool backPress, bool cancelOutside}){
showDialog<Null>(
context: context,
barrierDismissible: cancelOutside==null?true:cancelOutside,
builder: (BuildContext context) {
_loadingContext = context;
return WillPopScope(
child: LoadingDialog( //调用对话框
text: StringUtil.isEmpty(str)?'正在获取数据...':str,
),
onWillPop: () async {
return Future.value(backPress==null?true:backPress);
},
);
}
).then((value) => {//对话框关闭后
_loadingContext = null,
});
}
/*
* 关闭 公用的对话框-加载中...
*/
void hideLoading(){
if(_loadingContext != null) {//防止多次关闭
Navigator.pop(_loadingContext);
}
}
}
在对话框关闭后,将_loadingContext = null制空,防止在对话框未显示时调用关闭对话框方法,相当于Android中对话框判断dialog.isShowing()。
这里的StringUtil.isEmpty是判断字符串是否为空,全空格也为空:
/*
字符串是否为空
true 为空, false 不为空
*/
static bool isEmpty(String str){//正则匹配替换所有空白
return str==null || str.isEmpty || (str.replaceAll(new RegExp(r"\s+"), "")).isEmpty;
}
LoadingDialog是自定义的加载对话框:
class LoadingDialog extends Dialog {
String text;
LoadingDialog({Key key, @required this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return new Material(
type: MaterialType.transparency,
child: Center(
child: SizedBox(
width: 120.0,
height: 120.0,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0),),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircularProgressIndicator(),
Padding(
padding: const EdgeInsets.only(top: 20.0,),
child: new Text(text),
),
],
),
),
),
),
);
}
}
详解 Flutter State 生命周期:
https://blog.csdn.net/haha223545/article/details/105483176
Flutter - 自定义Dialog:
https://www.jianshu.com/p/4bbbb5aa855d