StatefulWidget 基本知識 1. 初始StatefulWidget 2. StatefulWidget簡單使用 3. 生命週期

本篇主要認識StatefulWidget、瞭解其狀態管理,簡單使用,生命週期等

1. 初始StatefulWidget

@immutable

看源碼,StatefulWidget也是繼承自Widget,其上有個註解:@immutable,該註解的意思就是不可變的意思,作用就是widget及其子類是不可變的,定義的變量必須是final的,所以,這就無法在StatelessWidget內實現數據更新操作。

官方@immutable解釋

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乾淨的了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章