一、前言:
Flutter中幾乎所有的對(duì)象都是一個(gè)Widget,StatelessWidget和StatefulWidget都是直接繼承自Widget類,而這兩個(gè)類也是Flutter中非常重要的兩個(gè)抽象類。我們做Flutter開發(fā)通常使用StatelessWidget和StatefulWidget,為了開發(fā)方便快捷,我們會(huì)封裝Widget基類。封裝基類我們首先要了解Widget生命周期,由于 StatelessWidget 的生命周期比較簡(jiǎn)單,它只有一個(gè) build 方法,我們先不考慮封裝,而StatefullWidget 的生命周期比較復(fù)雜,我們需要封裝StatefullWidget,這樣要使用StatefullWidget時(shí)我們直接繼承基類。
二、生命周期
initState:插入渲染樹時(shí)調(diào)用,只調(diào)用一次
didChangeDependencies:state依賴的對(duì)象發(fā)生變化時(shí)調(diào)用
didUpdateWidget:組件狀態(tài)改變時(shí)候調(diào)用,可能會(huì)調(diào)用多次
build:構(gòu)建Widget時(shí)調(diào)用
deactivate:當(dāng)移除渲染樹的時(shí)候調(diào)用
dispose:組件即將銷毀時(shí)調(diào)用
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
}
}
四、說明
我們重點(diǎn)關(guān)注了initState、build、dispose三個(gè)生命周期,其他生命周期也可自行封裝,這里是最簡(jiǎn)潔、僅針對(duì)核心生命周期的封裝。我們還可以拓展,將LoadingDialog、DefaultTextStyle、屏幕適配、Toast等封裝在基類。
例如LoadingDialog,代碼如下:
abstract class BaseState<T extends BasePage> extends State<T> {
BuildContext _loadingContext;
......省略
/*
* 顯示 公用的對(duì)話框-加載中...
* backPress:點(diǎn)擊返回,true消失,false不消失
* cancelOutside: 點(diǎn)擊加載框外區(qū)域,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( //調(diào)用對(duì)話框
text: StringUtil.isEmpty(str)?'正在獲取數(shù)據(jù)...':str,
),
onWillPop: () async {
return Future.value(backPress==null?true:backPress);
},
);
}
).then((value) => {//對(duì)話框關(guān)閉后
_loadingContext = null,
});
}
/*
* 關(guān)閉 公用的對(duì)話框-加載中...
*/
void hideLoading(){
if(_loadingContext != null) {//防止多次關(guān)閉
Navigator.pop(_loadingContext);
}
}
}
在對(duì)話框關(guān)閉后,將_loadingContext = null制空,防止在對(duì)話框未顯示時(shí)調(diào)用關(guān)閉對(duì)話框方法,相當(dāng)于Android中對(duì)話框判斷dialog.isShowing()。
這里的StringUtil.isEmpty是判斷字符串是否為空,全空格也為空:
/*
字符串是否為空
true 為空, false 不為空
*/
static bool isEmpty(String str){//正則匹配替換所有空白
return str==null || str.isEmpty || (str.replaceAll(new RegExp(r"\s+"), "")).isEmpty;
}
LoadingDialog是自定義的加載對(duì)話框:
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:
http://www.itdecent.cn/p/4bbbb5aa855d