老孟導讀:關於生命週期的文章共有2篇,第一篇是介紹 Flutter 中Stateful 組件的生命週期。博客地址:http://laomengit.com/blog/20201227/Stateful%E7%BB%84%E4%BB%B6%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.htmlhtml
第二篇是 Flutter 中與平臺相關的生命週期,git
博客地址:http://laomengit.com/blog/20201227/%E7%9B%B8%E5%85%B3%E5%B9%B3%E5%8F%B0%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.htmlgithub
博客中還有更多精彩文章,也歡迎加入 Flutter 交流羣。web
此篇文章介紹 StatefulWidget 組件的生命週期, StatefulWidget 組件的生命週期時很是重要的知識點,就像 Android 中 Activity 的生命週期同樣,不只在之後的工做中常常用到,面試也會常常被問到。面試
在 Flutter 中一切皆 組件,而組件又分爲 StatefulWidget(有狀態) 和 **StatelessWidget(無狀態)**組件 ,他們之間的區別是 StatelessWidget 組件發生變化時必須從新建立新的實例,而 StatefulWidget 組件則能夠直接改變當前組件的狀態而無需從新建立新的實例。微信
注意:使用的 Flutter 版本 和 Dart 版本以下:網絡
Flutter 1.22.4 • channel stable • https://github.com/flutter/flutter.git Framework • revision 1aafb3a8b9 (6 weeks ago) • 2020-11-13 09:59:28 -0800 Engine • revision 2c956a31c0 Tools • Dart 2.10.4框架
不一樣的版本 StatefulWidget 組件的生命週期會有差別。less
下面的 StatefulWidget 和 State 結構圖是StatefulWidget 組件生命週期的概覽,不一樣版本的差別也能夠對比此結構圖。編輯器

生命週期流程圖:

下面詳細介紹 StatefulWidget 組件的生命週期。
生命週期一:createState
下面是一個很是簡單的 StatefulWidget 組件:
class StatefulWidgetDemo extends StatefulWidget {
@override
_StatefulWidgetDemoState createState() => _StatefulWidgetDemoState();
}
class _StatefulWidgetDemoState extends State<StatefulWidgetDemo> {
@override
Widget build(BuildContext context) {
return Container();
}
}
當咱們構建一個 StatefulWidget 組件時,首先執行其構造函數(上面的代碼沒有顯示的構造函數,但有默認的無參構造函數),而後執行 createState 函數。但構造函數並非生命週期的一部分。
當 StatefulWidget 組件插入到組件樹中時 createState 函數由 Framework 調用,此函數在樹中給定的位置爲此組件建立 State,若是在組件樹的不一樣位置都插入了此組件,即建立了多個此組件,以下:
Row(children: [
MyStatefulWidget(),
MyStatefulWidget(),
MyStatefulWidget(),
],)
那麼系統會爲每個組件建立一個單獨的 State,當組件從組件樹中移除,而後從新插入到組件樹中時, createState 函數將會被調用建立一個新的 State。
createState 函數執行完畢後表示當前組件已經在組件樹中,此時有一個很是重要的屬性 mounted 被 Framework 設置爲 true。
生命週期二:initState
initState 函數在組件被插入樹中時被 Framework 調用(在 createState 以後),此函數只會被調用一次,子類一般會重寫此方法,在其中進行初始化操做,好比加載網絡數據,重寫此方法時必定要調用 super.initState(),以下:
@override
void initState() {
super.initState();
//初始化...
}
若是此組件須要訂閱通知,好比 ChangeNotifier 或者 Stream,則須要在不一樣的生命週期內正確處理訂閱和取消訂閱通知。
-
在 initState 中訂閱通知。 -
在 didUpdateWidget 中,若是須要替換舊組件,則在舊對象中取消訂閱,並在新對象中訂閱通知。 -
並在 dispose 中取消訂閱。
另外在此函數中不能調用 BuildContext.dependOnInheritedWidgetOfExactType,典型的錯誤寫法以下:
@override
void initState() {
super.initState();
IconTheme iconTheme = context.dependOnInheritedWidgetOfExactType<IconTheme>();
}
異常信息以下:

解決方案:
@override
void didChangeDependencies() {
super.didChangeDependencies();
context.dependOnInheritedWidgetOfExactType<IconTheme>();
}
上面的用法做爲初學者使用的比較少,但下面的錯誤代碼大部分應該都寫過:
@override
void initState() {
super.initState();
showDialog(context: context,builder: (context){
return AlertDialog();
});
}
異常信息以下:

解決方案:
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showDialog(context: context,builder: (context){
return AlertDialog(title: Text('AlertDialog'),);
});
});
}
注意:彈出 AlertDialog 在 didChangeDependencies 中調用也會出現異常,但和上面的異常不是同一個。
生命週期三:didChangeDependencies
didChangeDependencies 方法在 initState 以後由 Framework 當即調用。另外,當此 State 對象的依賴項更改時被調用,好比其所依賴的 InheritedWidget 發生變化時, Framework 會調用此方法通知組件發生變化。
此方法是生命週期中第一個可使用 BuildContext.dependOnInheritedWidgetOfExactType 的方法,此方法不多會被重寫,由於 Framework 會在依賴發生變化時調用 build,須要重寫此方法的場景是:依賴發生變化時須要作一些耗時任務,好比網絡請求數據。
didChangeDependencies 方法調用後,組件的狀態變爲 dirty,當即調用 build 方法。
生命週期四:build
此方法是咱們最熟悉的,在方法中建立各類組件,繪製到屏幕上。Framework會在多種狀況下調用此方法:
-
調用 initState 方法後。 -
調用 didUpdateWidget 方法後。 -
收到對 setState 的調用後。 -
此 State 對象的依存關係發生更改後(例如,依賴的 InheritedWidget 發生了更改)。 -
調用 deactivate 以後,而後將 State 對象從新插入樹的另外一個位置。
此方法能夠在每一幀中調用,此方法中應該只包含構建組件的代碼,不該該包含其餘額外的功能,尤爲是耗時任務。
生命週期五:didUpdateWidget
當組件的 configuration 發生變化時調用此函數,當父組件使用相同的 runtimeType 和 Widget.key 從新構建一個新的組件時,Framework 將更新此 State 對象的組件屬性以引用新的組件,而後使用先前的組件做爲參數調用此方法。
@override
void didUpdateWidget(covariant StatefulLifecycle oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget');
}
此方法中一般會用當前組件與前組件進行對比。Framework 調用完此方法後,會將組件設置爲 dirty 狀態,而後調用 build 方法,所以無需在此方法中調用 setState 方法。
生命週期六:deactivate
當框架從樹中移除此 State 對象時將會調用此方法,在某些狀況下,框架將從新插入 State 對象到樹的其餘位置(例如,若是包含該樹的子樹 State 對象從樹中的一個位置移植到另外一位置),框架將會調用 build 方法來提供 State 對象適應其在樹中的新位置。
生命週期七:dispose
當框架從樹中永久移除此 State 對象時將會調用此方法,與 deactivate 的區別是,deactivate 還能夠從新插入到樹中,而 dispose 表示此 State 對象永遠不會在 build。調用完 dispose後,mounted 屬性被設置爲 false,也表明組件生命週期的結束,此時再調用 setState 方法將會拋出異常。
子類重寫此方法,釋放相關資源,好比動畫等。
很是重要的幾個概念
下面介紹幾個很是重要的概念和方法,這些並非生命週期的一部分,可是生命週期過程當中的產物,與生命週期關係很是緊密。
mounted
mounted 是 State 對象中的一個屬性,此屬性表示當前組件是否在樹中,在建立 State 以後,調用 initState 以前,Framework 會將 State 和 BuildContext 進行關聯,當 Framework 調用 dispose 時,mounted 被設置爲 false,表示當前組件已經不在樹中。
createState 函數執行完畢後表示當前組件已經在組件樹中,屬性 mounted 被 Framework 設置爲 true,平時寫代碼時或者看其餘開源代碼時常常看到以下代碼:
if(mounted){
setState(() {
...
});
}
強烈建議:在調用 setState 時加上 mounted 判斷。
爲何要加上如此判斷?由於若是當前組件未插入到樹中或者已經從樹中移除時,調用 setState 會拋出異常,加上 mounted 判斷,則表示當前組件在樹中。
dirty 和 clean
dirty 表示組件當前的狀態爲 髒狀態,下一幀時將會執行 build 函數,調用 setState 方法或者 執行 didUpdateWidget 方法後,組件的狀態爲 dirty。
clean 與 dirty 相對應,clean 表示組件當前的狀態爲 乾淨狀態,clean 狀態下組件不會執行 build 函數。
setState
setState 方法是開發者常常調用的方法,此方法調用後,組件的狀態變爲 dirty,當有數據要更新時,調用此方法。
reassemble
reassemble 用於開發,好比 hot reload ,在 release 版本中不會回調此方法。


本文分享自微信公衆號 - 老孟Flutter(lao_meng_qd)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。