Flutter檢視Widget生命週期

白瑞德發表於2019-08-10

Flutter檢視Widget生命週期

作為一個Android開發者,一定會對Activity的生命週期有這很深刻的印象,而當你在使用Flutter時,其中Widget就是View,其生命週期就是從View建立到銷燬的過程。 Widget分為StatelessWidgetStatefulWidget 兩種,這兩種Widget的生命週期分別如下。

StatelessWidget的生命週期

無狀態Widget的生命週期很簡單,它只有一個生命週期:build

build

build函式用來構建檢視,每次頁面重新整理是被呼叫,典型的用法如下:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          child: new Text('Hello World'),
        ),
      ),
    );
  }
}
複製程式碼

StatefulWidget的生命週期

StatefulWidget生命週期整理如下圖:

Flutter檢視Widget生命週期
大致可分為三個階段:

  • 初始化:插入渲染樹
  • 執行中:在渲染樹中存在
  • 銷燬:從渲染樹中移除
初始化階段
createState

createState必須且僅執行一次,它用來建立state,當建立StatefulWidget時,該放方法被執行

initState

在建立StatefulWidget後,initState是第一個被呼叫的方法,同createState一樣只被呼叫一次,此時widget的被新增至渲染樹,mount的值會變為true,但並沒有渲染。可以在該方法內做一些初始化操作。在在override時要低啊用super.initState()

@override
void initState() {
  super.initState();
  ...
}
複製程式碼
didChangeDependencies

當widget第一次被建立時,didChangeDependencies緊跟著initState函式之後呼叫,在widget重新整理時,該方法不會被呼叫。它會在“依賴”發生變化時被Flutter Framework呼叫,這個依賴是指widget是否使用父widget中InheritedWidget的資料。也即是隻有在widget依賴的InheritedWidget發生變化之後,didChangeDependencies才會呼叫。 這種機制可以使子元件在所依賴的InheritedWidget變化時來更新自身!比如當主題、locale(語言)等發生變化時,依賴其的子widget的didChangeDependencies方法將會被呼叫。

//通過繼承InheritedWidget,將當前計數器點選次數儲存在ShareDataWidget的data屬性中:
class ShareDataWidget extends InheritedWidget {
  ShareDataWidget({
    @required this.data,
    Widget child
  }) :super(child: child);

  final int data; //需要在子樹中共享的資料,儲存點選次數

  //定義一個便捷方法,方便子樹中的widget獲取共享資料  
  static ShareDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }

  //該回撥決定當data發生變化時,是否通知子樹中依賴data的Widget  
  @override
  bool updateShouldNotify(ShareDataWidget old) {
    //如果返回true,則子樹中依賴(build函式中有呼叫)本widget
    //的子widget的`state.didChangeDependencies`會被呼叫
    return old.data != data;
  }
}

class _TestWidget extends StatefulWidget {
  @override
  __TestWidgetState createState() => new __TestWidgetState();
}
//然後我們實現一個子元件_TestWidget,在其build方法中引用ShareDataWidget中的資料。
class __TestWidgetState extends State<_TestWidget> {
  @override
  Widget build(BuildContext context) {
    //使用InheritedWidget中的共享資料
    return Text(ShareDataWidget
        .of(context)
        .data
        .toString());
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //父或祖先widget中的InheritedWidget改變(updateShouldNotify返回true)時會被呼叫。
    //如果build中沒有依賴InheritedWidget,則此回撥不會被呼叫。
    print("Dependencies change");
  }
}

複製程式碼

注意:如果_TestWidget的build方法中沒有使用ShareDataWidget的資料,那麼它的didChangeDependencies()將不會被呼叫,因為它並沒有依賴ShareDataWidget。在依賴改變之後build方法也會被呼叫,所以在大多數場景下都無需使用didChangeDependencies。然而如果你需要在依賴改變後執行一些昂貴的操作,比如網路請求,這時最好的方式就是在此方法中執行,這樣可以避免每次build()都執行這些昂貴操作。

build

build函式會在widget第一次建立時緊跟著didChangeDependencies方法之後和UI重新渲染是時呼叫。build只做widget的建立操作,如果在build裡做其他操作,會影響UI的渲染效果

執行中
didUpdateWidget

當元件的狀態改變的時候就會呼叫didUpdateWidget,比如呼叫了setState.

銷燬
deactivate

當要將State物件從渲染樹中移除的時候,就會呼叫 deactivate 生命週期,這標誌著 StatefulWidget將要銷燬。頁面切換時,也會呼叫它,因為此時State在檢視樹中的位置發生了變化但是State不會被銷燬,而是重新插入到渲染樹中。 重寫的時候必須要呼叫 super.deactivate()

dispose

從渲染樹中移除view的時候呼叫,State會永久的從渲染樹中移除,和initState正好相反mount值變味false。這時候就可以在dispose裡做一些取消監聽操作。

總結:
函式 呼叫次數 呼叫時間
createState 1 第一次建立
initState 1 第一次建立
didChangeDependencies n 第一次建立和依賴變化時
build n 第一次建立和UI重新渲染時
didUpdateWidget n 第一次建立和UI重新渲染時
deactivate n state物件將要移除時
dispose 1 state物件移除

相關文章