Flutter 源碼閲讀 - StatefulWidget 源碼分析 & State 生命週期

語言: CN / TW / HK

theme: cyanosis highlight: atom-one-light


開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第 6 天,點擊查看活動詳情

Flutter 源碼閲讀 - 三棵樹流程分析(一)

Flutter 源碼閲讀 - 三棵樹流程分析(二)

Flutter 源碼閲讀 - 三棵樹流程分析(三)


Flutter 源碼閲讀 - 三棵樹流程分析(三) 這篇文章中,筆者粗略的介紹了 StatefulWidget 的大致執行流程,並沒有進行深入的分析,這篇文章將深入分析一下它的源碼以及 State 的生命週期。

一、StatefulWidget

StatefulWidget 也是繼承自 Widget,重寫了 createElement,並且添加了一個新的接口 createState,下面我們看一下它的源碼:

image.png

看起來是不是很簡單,代碼不足十行。

  • createElement 方法返回一個 StatefulElement 類型的 Element
  • createState 抽象方法返回一個 State 類型的實例對象。在給定的位置為 StatefulWidget 創建可變狀態(state)。框架可以在 StatefulWidget生命週期內多次調用此方法,比如:將 StatefulWidget 插入到 Widget Tree 中的多個位置時,會創建多個單獨的 State 實例,如果將 StatefulWidgetWidget Tree 中刪除,稍後再次將琦插入到 Widget Tree 中,框架將會再次調用 createState創建一個新的 State 實例對象。

StatefulWidget 我們暫時就先講到這裏, 關於 StateStatefulElement 我們在下面會進行分析。


二、StatefulElement

上面講到 StatefulWidgetcreateElement 會創建一個 StatefulElement 類型的 Element。下面我們就一起看下 StatefulElement 的源碼。

image.png

在執行 StatefulWidget#createElement 時會把 this 傳遞進去,此時執行 StatefulElement 的構造方法中我們可以看出會做以下三件事情:

  • 首先通過 _state = widget.createState() 執行 StatefulWidget 中的 createState 進行闖將 State 實例;
  • 其次通過state._element = this 將當前對象賦值給 State 中的 _element 屬性;
  • 最後通過 state._widget = widget,將 StatefulWidget 賦值給 State 中的 _widget 屬性。

通過以上分析我們相應的可以得出以下結論:

  • StatefulElement 持有 State 狀態;
  • State 中又會反過來持有 StatefulElementStatefulWidget(當然,State 的源碼我們還沒有看到);
  • StatefulWidget 只是負責創建 StatefulElementState,但是並不持有它們。

至此我們已經理清了 StatefulWidgetStatefulElementState 三者之間的關係,關於 State 我們會在後面講到。現在我們已經知道 StatefulWidget 中的 createState 在何時執行,那麼 StatefulElement#createElement 又是在何時執行的呢?下面我們來看一個例子:

```dart import 'package:flutter/material.dart';

void main() { runApp( const MyApp(), ); }

class MyApp extends StatefulWidget { const MyApp({super.key});

@override State createState() => _MyAppState(); }

class _MyAppState extends State { @override Widget build(BuildContext context) { return const ColoredBox( color: Colors.red, ); } } ```

image.png

通過斷點調試可以看出在 Element#inflateWidget 中 通過 newWidget.createElement() 來進行觸發 StatefulWidget#createElement 的執行,進而執行 StatefulElement 的構造函數。

關於更多 StatefulElement 內部方法,將在 State 源碼以及相關案例中穿插進行。


三、State

State 是一個抽象類,它只定義了一個 build 抽象方法,由於構建 Widget 對象。它是通過StatefulElement#build 方法進行調用的。

image.png

如下是 State 源碼的部分截圖:

image.png

從源碼中我們也可以對上面的結論得到驗證,State 持有 StatefulElementStatefulWidget,這裏的泛型 T必須是 StatefulWidget 類型,如下圖所示:

image.png

除此之外 State 中還持有 BuildContext,通過源碼我們可以看出 BuildContext 其實就是 StatefulElement

dart BuildContext get context { return _element!; }

那麼現在我們可以思考一下 State 中的生命週期方法在何時調用以及在哪裏調用呢?從上面我們得出的結論:StatefulElement 持有 State 狀態,State 中又會反過來持有 StatefulElementStatefulWidgetStatefulWidget 只是負責創建 StatefulElementState,但是並不持有它們。不難猜測出,應該是在 StatefulElement 中來觸發的,下面我通過一個小的案例來進行研究一下:

dart void main() { runApp( const WrapWidget(), ); }

```dart class WrapWidget extends StatelessWidget { const WrapWidget({ Key? key, }) : super(key: key);

@override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text("StatefulWidget Demo"), ), body: MyApp(), ), ); } } ```

```dart class MyApp extends StatefulWidget { const MyApp({ super.key, });

@override // ignore: no_logic_in_create_state State createState() { debugPrint("createState"); return _MyAppState(); } }

class _MyAppState extends State { late int _count = 0;

@override void initState() { debugPrint("initState"); super.initState(); }

@override void didChangeDependencies() { debugPrint("didChangeDependencies"); super.didChangeDependencies(); }

@override void didUpdateWidget(MyApp oldWidget) { debugPrint("didUpdateWidget"); super.didUpdateWidget(oldWidget); }

@override void deactivate() { debugPrint("deactivate "); super.deactivate(); }

@override void dispose() { debugPrint("dispose"); super.dispose(); }

@override void reassemble() { debugPrint("reassemble"); super.reassemble(); }

@override Widget build(BuildContext context) { debugPrint("build"); return Column( children: [ Text('$_count'), OutlinedButton( onPressed: () { setState(() { _count++; }); }, child: const Text('OnPress'), ), ], ); } } ```

程序剛運行時打印日誌如下:

image.png

然後我們點擊⚡️按鈕熱重載,控制枱輸出日誌如下:

image.png

我們再次點擊 OnPress 按鈕時,打印日誌如下:

image.png

此時我們註釋掉 WrapWidget 中的 body: MyApp() 這行代碼,打印日誌如下:

image.png

此時結合源碼,我們來一起看下各個生命週期函數:

  • initState: 當 Widget 第一次插入到 Widget Tree中,會執行一次,我們一般在這裏可以做一些初始化狀態的操作以及訂閲通知事件等,通過源碼我們可以看出它是在 Statefulelement#_firstBuild 中執行的;

    image.png

  • didChangeDependencies: 當 State 對象的依賴發生變化時會進行調用,例如:例如系統語言 Locale 或者應用主題等,通過源碼我們可以看出它在 Statefulelement#_firstBuildStatefulelement#performRebuild 中都會執行;

    image.png

  • build:在以下場景中都會調用:

    • initState 調用之後
    • didUpdateWidget 調用之後
    • setState 調用之後
    • didChangeDependencies 調用之後
    • 調用 deactivate 之後,然後又重新插入到 Widtget Tree

    通過源碼可以看出它是在 Statefulelement#build 中執行的;

    image.png

  • reassemble:專門為了開發調試而提供的,在 hot reload 時會被調用,在 Release 模式下永遠不會被調用,通過源碼可以看出它是在 Statefulelement#reassemble 中執行的;

    image.png

  • didUpdateWidget:在 Widget 重新構建時,Flutter 框架會在 Element#updateChild 中通過Widget.canUpdate 判斷是否需要進行更新,如果為 true 則進行更新;

    image.png

    canUpdate 源碼中,新舊 widget 的 key 和 runtimeType 同時相等時會返回 true,也就是説在在新舊 widgetkeyruntimeType 同時相等時 didUpdateWidget() 就會被調用;

    image.png

    image.png

  • deactivate:當 State 對象從樹中被移除時將會調用,它將會在 Statefulelement#deactivate 中進行調用;

    image.png

  • dispose:當 State 對象從樹中被永久移除時調用;通常在此回調中釋放資源,它將會在 Statefulelement#unmount 中進行調用。

    image.png


總結

至此,結合一些小的案例和源碼閲讀,我們大致明白了 StatefulWidgetState 以及 StatefulElement 他們三者之間的關係以及 State 的生命週期,相信在以後的實際應用中會更加得心應手。