前言:生命週期是一個組件加載到卸載的整個週期,熟悉生命週期可讓咱們在合適的時機作該作的事情,
flutter中的State生命週期和android以及React Native的生命週期相似。javascript
先看一張生命週期的流程圖:java
大體能夠分爲3個階段:android
初始化
狀態變化
組件移除
初始化
State初始化時會依次執行 : 構造函數 > initState > didChangeDependencies > Widget build , 此時頁面加載完成。app
而後咱們看一下每一個函數的意義:框架
構造函數
調用次數:1次ide
這個函數嚴格意義上來說不屬於生命週期的一部分,由於這個時候State的widget屬性爲空,沒法在構造函數中訪問widget的屬性 。可是構造函數必然是要第一個調用的。能夠在這一部分接收前一個頁面傳遞過來的數據。函數
initState
Called when this object is inserted into the tree.佈局
調用次數:1次ui
當插入渲染樹的時候調用,這個函數在生命週期中只調用一次。這裏能夠作一些初始化工做,好比初始化State的變量。this
didChangeDependencies
Called when a dependency of this [State] object changes.
初始化時,在initState()以後馬上調用
當依賴的InheritedWidget rebuild,會觸發此接口被調用
這個函數會緊跟在initState以後調用,而且能夠調用BuildContext.inheritFromWidgetOfExactType,那麼BuildContext.inheritFromWidgetOfExactType的使用場景是什麼呢?最經典的應用場景是
new DefaultTabController(length: 3, child: new TabBar( tabs: [ "主頁","訂單","個人" ] .map( (data)=>new Text(data) ).toList(),
TabBar原本須要定義一個TabController,可是在外面套一層DefaultTabController就不須要定義TabContrller了,看下源碼:
@override void didChangeDependencies() { super.didChangeDependencies(); _updateTabController(); _initIndicatorPainter(); } void _updateTabController() { final TabController newController = widget.controller ?? DefaultTabController.of(context); ... }
注意到這裏DefaultTabController.of(context)
static TabController of(BuildContext context) { final _TabControllerScope scope = context.inheritFromWidgetOfExactType(_TabControllerScope); return scope?.controller; }
實際上就是調用BuildContext.inheritFromWidgetOfExactType,也就說在didChangeDependencies中,能夠跨組件拿到數據。
#運行時
build
調用次數:屢次
初始化以後開始繪製界面,當setState觸發的時候會再次被調用
didUpdateWidget
Called whenever the widget configuration changes.
祖先節點rebuild widget時調用 .當組件的狀態改變的時候就會調用didUpdateWidget.
理論上setState的時候會調用,但我實際操做的時候發現只是作setState的操做的時候沒有調用這個方法。而在我改變代碼hot reload時候會調用 didUpdateWidget 並執行 build…
實際上這裏flutter框架會建立一個新的Widget,綁定本State,並在這個函數中傳遞老的Widget。
這個函數通常用於比較新、老Widget,看看哪些屬性改變了,並對State作一些調整。
須要注意的是,涉及到controller的變動,須要在這個函數中移除老的controller的監聽,並建立新controller的監聽。
組件移除
組件移除,例如頁面銷燬的時候會依次執行:deactivate > dispose
deactivate
Called when this object is removed from the tree.
在dispose以前,會調用這個函數。實測在組件可見狀態變化的時候會調用,當組件卸載時也會先一步dispose調用。
dispose
Called when this object is removed from the tree permanently.
調用次數:1次
一旦到這個階段,組件就要被銷燬了,這個函數通常會移除監聽,清理環境。
##reassemble
hot reload調用
名稱 | 狀態 |
---|---|
initState | 插入渲染樹時調用,只調用一次 |
didChangeDependencies | state依賴的對象發生變化時調用 |
didUpdateWidget | 組件狀態改變時候調用,可能會調用屢次 |
build | 構建Widget時調用 |
deactivate | 當移除渲染樹的時候調用 |
dispose | 組件即將銷燬時調用 |
實際場景
假設咱們從A頁面跳轉到B頁面, 那麼A,B頁面的生命週期會是怎樣的呢?
B頁面進入初始化狀態,依次執行4個函數:構造函數 > initState > didChangeDependencies > Widget build , 此時頁面加載完成,進入運行態。
此時A頁面依次執行deactivate > build函數。注意 此時A頁面並未卸載。
而後咱們假設B頁面只有一個按鈕,點擊B頁面中的按鈕,改變按鈕的文字,會執行widget的build方法 ,(理論上也應該執行didUpdateWidget,但我這裏沒有)。
這時,咱們點擊返回鍵從B頁面返回到A頁面。
A頁面從新顯示,B頁面開始卸載。
那麼A先執行deactivate > build , 而後B頁面依次執行:deactivate > dispose 。
此時A頁面進入運行態,B頁面移除。
本次示例B頁面代碼:
import 'package:flutter/material.dart'; class NewsDetailPage extends StatefulWidget { @override State<StatefulWidget> createState() => NewsDetailState(); } class NewsDetailState extends State<NewsDetailPage> { int text = 1; NewsDetailState() { print('構造函數'); } @override void initState() { print('init state'); super.initState(); } @override void didChangeDependencies() { print('didChangeDependencies'); super.didChangeDependencies(); } @override Widget build(BuildContext context) { print('widget build'); return Scaffold( body: Center( child: _loading(), ), appBar: AppBar( title: Text('諮詢詳情'), ), ); } @override void didUpdateWidget(NewsDetailPage oldWidget) { print('組件狀態改變:didUpdateWidget'); super.didUpdateWidget(oldWidget); } @override void deactivate() { print('移除時:deactivate'); super.deactivate(); } @override void dispose() { print('移除時:dispose'); super.dispose(); } //預加載佈局 Widget _loading() { return Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CircularProgressIndicator( strokeWidth: 1.0, ), Container( child: Text("正在加載"), margin: EdgeInsets.only(top: 10.0), ) ], ); } }
Tips:
下面內容來自鹹魚技術團隊.
當ListView中的item滾動出可顯示區域的時候,item會被從樹中remove掉,此item子樹中全部的state都會被dispose,state記錄的數據都會銷燬,item滾動回可顯示區域時,會從新建立全新的state、element、renderobject
使用hot reload功能時,要特別注意state實例是沒有從新建立的,若是該state中存在一下複雜的資源更新須要從新加載才能生效,那麼須要在reassemble()添加處理,否則當你使用hot reload時候可能會出現一些意想不到的結果,例如,要將顯示本地文件的內容到屏幕上,當你開發過程當中,替換了文件中的內容,可是hot reload沒有觸發從新讀取文件內容,頁面顯示仍是原來的舊內容.
didChangeDependencies有兩種狀況會被調用。
建立時候在initState 以後被調用
在依賴的InheritedWidget發生變化的時候會被調用
正常的退出流程中會執行deactivate而後執行dispose。可是也會出現deactivate之後不執行dispose,直接加入樹中的另外一個節點的狀況。
這裏的狀態改變包括兩種可能:1.經過setState內容改變 2.父節點的state狀態改變,致使孩子節點的同步變化。
App生命週期
須要指出的是若是想要知道App的生命週期,那麼須要經過WidgetsBindingObserver的didChangeAppLifecycleState 來獲取。經過該接口能夠獲取是生命週期在AppLifecycleState類中。經常使用狀態包含以下幾個:
名稱 | 狀態 |
---|---|
resumed | 可見並能響應用戶的輸入 |
inactive | 處在並不活動狀態,沒法處理用戶響應 |
paused | 不可見並不能響應用戶的輸入,可是在後臺繼續活動中 |
一個實際場景中的例子:
在不考慮suspending的狀況下:從後臺切入前臺生命週期變化以下: AppLifecycleState.inactive->AppLifecycleState.resumed;
從前臺壓後臺生命週期變化以下: AppLifecycleState.inactive->AppLifecycleState.paused;
原文:https://blog.csdn.net/u011272795/article/details/82695920