本篇主要認識StatefulWidget、瞭解其狀態管理,簡單使用,生命週期等
1. 初始StatefulWidget
@immutable
看源碼,StatefulWidget也是繼承自Widget,其上有個註解:@immutable
,該註解的意思就是不可變的意思,作用就是widget及其子類是不可變的,定義的變量必須是final的,所以,這就無法在StatelessWidget內實現數據更新操作。
1.1. StatefulWidget 狀態管理
StatefulWidget是相對於StatelessWidget實現可變的widget,StatefulWidget也是繼承於widget,所以,自然在widget內是做不了變量更新的
既然Widget是不可變,那麼StatefulWidget如何來存儲可變的狀態呢?
- StatelessWidget無所謂,因爲它裏面的數據通常是直接定義完後就不修改的。
- 但StatefulWidget需要有狀態(可以理解成變量)的改變,這如何做到呢?
Flutter將StatefulWidget設計成了兩個類:
- 也就是你創建StatefulWidget時必須創建兩個類:
- 一個類繼承自StatefulWidget,作爲Widget樹的一部分;
- 一個類繼承自State,用於記錄StatefulWidget會變化的狀態,並且根據狀態的變化,構建出新的Widget;
基本結構如下:
class MyStatefulWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// 將創建的State返回
return MyState();
}
}
class MyState extends State<MyStatefulWidget> {
@override
Widget build(BuildContext context) {
return <構建自己的Widget>;
}
}
2. StatefulWidget簡單使用
實現效果:
代碼:
class HomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("HomeBody build");
return MyCounterWidget();
}
}
class MyCounterWidget extends StatefulWidget {
MyCounterWidget() {
print("執行了MyCounterWidget的構造方法");
}
@override
State<StatefulWidget> createState() {
print("執行了MyCounterWidget的createState方法");
// 將創建的State返回
return MyCounterState();
}
}
class MyCounterState extends State<MyCounterWidget> {
int counter = 0;
MyCounterState() {
print("執行MyCounterState的構造方法");
}
@override
void initState() {
super.initState();
print("執行MyCounterState的init方法");
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("執行MyCounterState的didChangeDependencies方法");
}
@override
Widget build(BuildContext context) {
print("執行執行MyCounterState的build方法");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
color: Colors.redAccent,
child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
onPressed: () {
setState(() {
counter++;
});
},
),
RaisedButton(
color: Colors.orangeAccent,
child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
onPressed: () {
setState(() {
counter--;
});
},
)
],
),
Text("當前計數:$counter", style: TextStyle(fontSize: 30),)
],
),
);
}
@override
void didUpdateWidget(MyCounterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("執行MyCounterState的didUpdateWidget方法");
}
@override
void dispose() {
super.dispose();
print("執行MyCounterState的dispose方法");
}
}
分析
- 自定義的State做狀態管理,關於state不解釋,前端coder肯定懂,我主程是客戶端開發
- setState做狀態更新:更新完會觸發widget重新build
- 記住flutter自定義可變widget的套路:
- 自定義widget:繼承StatefulWidget,重載build加載自定義的state
- 自定義state:繼承State,實現你真正的ui部分和狀態(data)管理
- 快捷代碼模塊:stless、stful 就會自動不全代碼
3. 生命週期
不多解釋,既然是個對象,就有生命流程,生命週期也是ui開發數據管理的基本着力點。
- StatelessWidget可以由父Widget直接傳入值,調用build方法來構建,整個過程非常簡單;
- 而StatefulWidget需要通過State來管理其數據,並且還要監控狀態的改變決定是否重新build整個Widget;
- 所以,我們主要討論StatefulWidget的生命週期,也就是它從創建到銷燬的整個過程;
過程
首先,執行StatefulWidget中相關的方法:
- 1、執行StatefulWidget的構造函數(Constructor)來創建出StatefulWidget;
- 2、執行StatefulWidget的createState方法,來創建一個維護StatefulWidget的State對象;
其次,調用createState創建State對象時,執行State類的相關方法:
1、執行State類的構造方法(Constructor)來創建State對象;
-
2、執行initState,我們通常會在這個方法中執行一些數據初始化的操作,或者也可能會發送網絡請求;
- 注意:這個方法是重寫父類的方法,必須調用super,因爲父類中會進行一些其他操作;
- 並且如果你閱讀源碼,你會發現這裏有一個註解(annotation):@mustCallSuper
-
3、執行didChangeDependencies方法,這個方法在兩種情況下會調用
- 情況一:調用initState會調用;
- 情況二:從其他對象中依賴一些數據發生改變時,比如前面我們提到的InheritedWidget(這個後面會講到);
4、Flutter執行build方法,來看一下我們當前的Widget需要渲染哪些Widget;
5、當前的Widget不再使用時,會調用dispose進行銷燬;
6、手動調用setState方法,會根據最新的狀態(數據)來重新調用build方法,構建對應的Widgets;
7、執行didUpdateWidget方法是在當父Widget觸發重建(rebuild)時,系統會調用didUpdateWidget方法;
上面的代碼直接運行,打印如下:
flutter: HomeBottomBar 構造函數
flutter: HomeBody build
flutter: 執行了MyCounterWidget的構造方法
flutter: 執行了MyCounterWidget的createState方法
flutter: 執行MyCounterState的構造方法
flutter: 執行MyCounterState的init方法
flutter: 執行MyCounterState的didChangeDependencies方法
flutter: 執行執行MyCounterState的build方法
flutter: HomeBottomBar 構造函數
// 注意:Flutter會build所有的組件兩次,如果你是用as開發的話,會有這個問題,但是vscode是正常的,或者flutter run 方法啓動,也是隻調用一遍的,所以,不用太在意,可以理解爲是as的bug,運行到手機上肯定是正常的
flutter: HomeBody build
flutter: 執行了MyCounterWidget的構造方法
flutter: 執行MyCounterState的didUpdateWidget方法
flutter: 執行執行MyCounterState的build方法
//當我們改變狀態,手動執行setState方法後會打印如下結果:
flutter: 執行執行MyCounterState的build方法
3.1. 深入分析
mounted
/// Whether this [State] object is currently in a tree.
///
/// After creating a [State] object and before calling [initState], the
/// framework "mounts" the [State] object by associating it with a
/// [BuildContext]. The [State] object remains mounted until the framework
/// calls [dispose], after which time the framework will never ask the [State]
/// object to [build] again.
///
/// It is an error to call [setState] unless [mounted] is true.
bool get mounted => _element != null;
1、第一句:用於判斷state是否在widget樹中,也就是判斷widget是否加載
2、最後一句:widget加載之前,是不能調用setState的。
3、其他,表示state掛載原理和生命週期
所以,開發中,一般開發中,更新狀態的時候,會有widget是否加載的判斷,以免報錯:
if(mounted){
setState((){
})
}
dirty state & clean state
dirty state
- 它實際是通過一個Element的東西的屬性來標記的;
- 將它標記爲dirty會等待下一次的重繪檢查,強制調用build方法來構建我們的Widget;
clean state的含義是乾淨的State
- 它表示當前build出來的Widget,下一次重繪檢查時不需要重新build;
所以,流程就是,當setState調用的時候,_element!.markNeedsBuild();
將widget標記爲需要build將dirty=true
,然後調用scheduleBuildFor將widget加入dirty的widget列表等待rebuild,然後觸發更新去rebuild,完後,將widget的dirty=false
,這時候state就是clean乾淨的了