Flutter 生命周期包括了組件的生命周期以及App的生命周期。
一、組件生命周期
一個flutter組件主要分為無狀態組件(StatelessWidget)和有狀態組件(StatefulWidget),無狀態組件只在創建的時候渲染一次,創建完成后不能重新渲染,而有狀態組件可以根據交互或數據變化等,進行多次渲染,本文組件生命周期專指有狀態組件的生命周期。
組件生命周期主要是:createState、initState、didChangeDependencies、reassemble、didUpdateWidget、build、deactivate、dispose這幾個函數,從函數名我們可以初步感知其作用,它們大致的流轉關系及作用如圖:
下面以一個簡單的Demo驗證這個流程
1.構建一個簡單的MyApp:
import 'package:flutter/material.dart';
import 'package:flutterlifecycledemo/ui/home_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
/// 初始化state,只會在第一次構建組件時調用一次
///
/// 在此期間可以執行State各變量的初始賦值
/// 同時可以在此處調用網絡請求,服務器返回數據后通過setState刷新頁面,類似于iOS中的ViewDidLoad方法
@override
void initState() {
super.initState();
print('initState_MyApp');
}
/// 該組件所依賴的State發生變化時觸發,或者在initState(組件首次創建)后觸發
///
/// 該組件所依賴的State指全局的State,例如語言和主題等
/// 此方法會觸發build
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies_MyApp');
}
/// 在debug模式下,每次熱重載都會觸發該函數
///
/// 主要是在開發階段在此增加一些debug代碼,檢查代碼問題
@override
void reassemble() {
super.reassemble();
print('reassemble_MyApp');
}
/// 組件重新構建(如熱重載),父組件發生build的情況下觸發
///
/// 注意:
/// 父組件發生build,本組件(子組件)該方法才會被調用,并且該方法調用后一定會再調用本組件中的build方法
/// 并且父組件首次創建不會觸發
@override
void didUpdateWidget(MyApp oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget_HomePage');
}
/// 組件在被移除節點后被調用
///
/// 如果該組件被移除節點,然后未被插入到其他節點時,則會繼續調用dispose永久移除
@override
void deactivate() {
super.deactivate();
print('deactivate_MyApp');
}
/// 永久移除組件,釋放組件資源
@override
void dispose() {
super.dispose();
print('dispose_MyApp');
}
/// 構建需要渲染的widget,會被多次調用(會被setState/didChangeDependencies/didUpdateWidget觸發)
///
/// 只做返回Widget的相關邏輯
@override
Widget build(BuildContext context) {
print('build_MyApp');
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Color(0xff409eff),
brightness: Brightness.light, // 默認狀態欄顏色
),
home: HomePage(),
);
}
}
2.HomPage實現:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
HomePage();
/// 創建state
///
/// 當StatefulWidget被調用時會立即執行
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool showProgressHud = true;
double cw = 60;
/// 模擬加載數據
void loadData(){
// 5秒后調用setState
Future.delayed(Duration(seconds: 5), (){
setState(() {
showProgressHud = false;
});
});
}
/// 初始化state,只會在第一次構建組件時調用一次
///
/// 在此期間可以執行State各變量的初始賦值
/// 同時可以在此處調用網絡請求,服務器返回數據后通過setState刷新頁面,類似于iOS中的ViewDidLoad方法
@override
void initState() {
super.initState();
print('initState_HomePage');
loadData();
}
/// 該組件所依賴的State發生變化時觸發,或者在initState(組件首次創建)后觸發
///
/// 該組件所依賴的State指全局的State,例如語言和主題等
/// 此方法會觸發build
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies_HomePage');
}
/// 在debug模式下,每次熱重載都會觸發該函數
///
/// 主要是在開發階段在此增加一些debug代碼,檢查代碼問題
@override
void reassemble() {
super.reassemble();
print('reassemble_HomePage');
}
/// 組件重新構建(如熱重載),父組件發生build的情況下觸發
///
/// 注意:
/// 父組件發生build,本組件(子組件)該方法才會被調用,并且該方法調用后一定會再調用本組件中的build方法
/// 并且父組件首次創建不會觸發
@override
void didUpdateWidget(HomePage oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget_HomePage');
}
/// 組件在被移除節點后被調用
///
/// 如果該組件被移除節點,然后未被插入到其他節點時,則會繼續調用dispose永久移除
@override
void deactivate() {
super.deactivate();
print('deactivate_HomePage');
}
/// 永久移除組件,釋放組件資源
@override
void dispose() {
super.dispose();
print('dispose_HomePage');
}
/// 構建需要渲染的widget,會被多次調用(會被setState/didChangeDependencies/didUpdateWidget觸發)
///
/// 只做返回Widget的相關邏輯
@override
Widget build(BuildContext context) {
print('build_HomePage');
return Scaffold(
appBar: AppBar(
title: Text('Hello Flutter',style: TextStyle(color: Color(0xffffffff),fontSize: 16, fontWeight: FontWeight.bold),),
centerTitle: true,
actions: <Widget>[
FlatButton( // 觸發setState,showProgressHud=true,當前HomeConent組件會被移除
child: Text('loadData',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
showProgressHud = true;
loadData();
});
},
),
FlatButton( // 增加cw值,并只觸發setState,showProgressHud不變,當前SubHomePage組件不會被移除
child: Text('click',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
cw += 10;
});
},
),
],
),
body: Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
),
child: Center(
child: showProgressHud ?
CircularProgressIndicator() :
Column(
mainAxisAlignment: MainAxisAlignment.center ,
children: <Widget>[
Container(width: cw, height: 44,color: Color(0xff889eff),),
SubHomePage()
],
)
,
),
),
);
}
}
3.SubHomePage實現:
class SubHomePage extends StatefulWidget {
@override
_SubHomePageState createState() => _SubHomePageState();
}
class _SubHomePageState extends State<SubHomePage> {
/// 初始化state,只會在第一次構建組件時調用一次
///
/// 在此期間可以執行State各變量的初始賦值
/// 同時可以在此處調用網絡請求,服務器返回數據后通過setState刷新頁面,類似于iOS中的ViewDidLoad方法
@override
void initState() {
super.initState();
print('initState_SubHomePage');
}
/// 該組件所依賴的State發生變化時觸發,或者在initState(組件首次創建)后觸發
///
/// 該組件所依賴的State指全局的State,例如語言和主題等
/// 子類很少重寫這個方法,非必要情況不用重寫此方法
/// 此方法會觸發build
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies_SubHomePage');
}
/// 在debug模式下,每次熱重載都會觸發該函數
///
/// 主要是在開發階段在此增加一些debug代碼,檢查代碼問題
@override
void reassemble() {
super.reassemble();
print('reassemble_SubHomePage');
}
/// 組件重新構建(如熱重載),父組件發生build的情況下觸發
///
/// 注意:
/// 父組件發生build,本組件(子組件)該方法才會被調用,并且該方法調用后一定會再調用本組件中的build方法
/// 并且父組件首次創建不會觸發
@override
void didUpdateWidget(SubHomePage oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget_SubHomePage');
}
/// 組件在被移除節點后被調用
///
/// 如果該組件被移除節點,然后未被插入到其他節點時,則會繼續調用dispose永久移除
@override
void deactivate() {
super.deactivate();
print('deactivate_SubHomePage');
}
/// 永久移除組件,釋放組件資源
@override
void dispose() {
super.dispose();
print('dispose_SubHomePage');
}
/// 構建需要渲染的widget,會被多次調用(會被setState/didChangeDependencies/didUpdateWidget觸發)
///
/// 只做返回Widget的相關邏輯
@override
Widget build(BuildContext context) {
print('build_SubHomePage');
return Column(
children: <Widget>[
Container(height: 100,),
Text('Welcome to flutter', style: TextStyle(color: Color(0xff333333), fontSize: 16),),
Container(
height: 10,
),
FlatButton(
onPressed: increaseAction,
child: Text(
'Increase',
style: TextStyle(color: Color(0xffffffff), fontSize: 14),
),
color: Color(0xff409eff),
)
],
);
}
void increaseAction(){
setState(() {
});
}
}
頁面效果:
首次加載控制臺輸出:
正好驗證了首次加載有狀態組件的生命周期,其中build_HomePage打印了兩次,是因為HomePage在initState的時候調用了模擬網絡請求方法loadData,在請求完畢后調用了setState,從而觸發了HomePage的build。
接著點擊導航欄上的loadData(屬于HomePage的組件)按鈕,觸發:
FlatButton( // 觸發setState,showProgressHud=true,當前SubHomePage組件會被移除
child: Text('loadData',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
showProgressHud = true;
loadData();
});
},
),
此時控制臺輸出:
我們發現首次生成的SubHomePage被移除了,并且loadData完成后生成新的SubHomePage,新的SubHomePage會接著走新的首次加載流程
child: Center(
child: showProgressHud ?
CircularProgressIndicator() :
Column(
mainAxisAlignment: MainAxisAlignment.center ,
children: <Widget>[
Container(width: cw, height: 44,color: Color(0xff889eff),),
SubHomePage()
],
)
,
),
此時我們點HomePage導航欄上的click按鈕 ,增加矩形的寬度:
FlatButton( // 增加cw值,并只觸發setState,showProgressHud不變,當前SubHomePage組件不會被移除
child: Text('click',style: TextStyle(color: Color(0xffffffff),fontSize: 14),),
onPressed: (){
setState(() {
cw += 10;
});
},
),
控制臺輸出:
此時我們可以看到HomePage的build觸發了其子組件SubHomePage的didUpdateWidget和build方法,但是SubHomePage仍然是原來的SubHomePage,并未重新釋放(如果釋放后重構會調用dispose和initState),只是重新build了一次。
點擊SubHomePage上的increase,控制臺輸出:
cmd+\ 熱重載,控制臺輸出:
二、App生命周期
app生命周期比較簡單,具體可以參考官方文檔