iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 20
1
Mobile Development

iOS Developer Learning Flutter系列 第 20

iOS Developer Learning Flutter. Lesson19 Life Cycle

Today Preview

1. State Life Cycle

以前我們在iOS或Android研究生命週期時
通常研究的對象是ViewController或Activity/Fragment
不過在Flutter,由於「Everything is a Widget」
所以我們這次討論的對象是Widget

然而Widget有分兩種:善變的StatefulWidget跟穩重的StatelessWidget
既然stless這麼穩重當然不必多言
那麼我們就把焦點放在stful身上

1.1 簡單版

先上一張圖
來源(聽說是官方的,但不可考)

這張圖說明了一個StatefulWidget build完之後
除了dispose之外
可能有兩條路

  1. 就是我們最常見的setState
    它去改變了它自己內部的狀態
  2. 或是它接收到了新的組態
    然後call didUpdateWidget

而不管是哪條路
之後都會再call一次build

1.2 完整版

再來一張
出處:Flutter實戰 > 第三章:基礎組件 > 3.1 Widget簡介 > 3.1.6 State

我把它們分成三類,簡短解讀如下(文末有對照表)

  1. A 創建類
    1. initState
      widget創建後初始化(只會呼叫一次)
    2. didChangeDependencies
      當依賴對象改變後呼叫(就算沒有依賴對象在initState後也會呼叫一次)
      與InheritedWidget有關(明天會提到)
    3. didUpdateWidget
      字面上的意思是已經更新
      更新 = 使用舊的widget
      我這邊也是頭有點昏
      下面會再補充
    4. build
      建立widget
  2. D 調試類
    1. reassemble
      hot relaod後呼叫,Release模式下就一定不會被呼叫(官方說明)
  3. Z 釋放類
    1. deactivate
      當widget從UI樹中被移出後呼叫
    2. dispose
      當deactivate後, 如果沒有被加回UI樹中
      就會被永久移除

1.3 didUpdateWidget

widget lifecycle中的各個步驟我覺得都很明確
只有這個didUpdateWidget我覺得有點混亂
以下根據各方面說明

  1. Flutter source code
//Widget這個Class裡面有個canUpdate
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
  1. Flutter實戰的解釋

Key: 这个key属性类似于React/Vue中的key,主要的作用是决定是否在下一次build时复用旧的widget,决定的条件在canUpdate()方法中。

canUpdate是一个静态方法,它主要用于在Widget树重新build时复用旧的widget,
其实具体来说,应该是:是否用新的Widget对象去更新旧UI树上所对应的Element对象的配置;
通过其源码我们可以看到,只要newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。

didUpdateWidget():在widget重新构建时,Flutter framework会调用Widget.canUpdate来检测Widget树中同一位置的新旧节点,然后决定是否需要更新,
如果Widget.canUpdate返回true则会调用此回调。
也就是说在在新旧widget的key和runtimeType同时相等时didUpdateWidget()就会被调用。

  1. 我的實驗結果
    1. 觸發條件一定是父widget build之後
    2. 只要父widget有build,就算setState裡面什麼都沒寫也會觸發
    3. 如果事先先把子widget存成變數,就不會呼叫
    4. 雖然存成變數,但如果根據setState回傳不同的子widget,又會呼叫了

1.4 life cycle with child

Today Preview裡面示範的是單一widget自己本身life cycle呼叫的順序
下面幾張圖呈現了不同階段自己與child的life cycle (pls開頭的就是child XD)

  1. 創建期
  2. 刷新期
  3. 釋放期

2. App Life Cycle

這部分Flutter跟Android比較像
並沒有一個App級的通知
而是由各頁面各自處理⚠️⚠️⚠️

另外比較大的差異
就是Flutter沒有will/did的概念⚠️⚠️⚠️
它都是did
以AppLifecycleState這個enum來說
總共有四個狀態

  1. resumed 前景
  2. inactive (前後景切換中, is not receiving user input, iOS獨享)
  3. paused 後景
  4. detached (據說是跟onStop相同效果, Android獨享)

所以如果想要做到跟一些金融App一樣
要進背景前噴霧一下>////<
看起來是沒辦法直接靠AppLifecycleState做到
所以Today Preview展示了回前景後驗證的動作
只要四步驟即可:

  1. YourWidgetState with WidgetsBindingObserver 這個interface
  2. initState時call WidgetsBinding.instance.addObserver(this)
  3. override void didChangeAppLifecycleState(AppLifecycleState state)
  4. dispose時call WidgetsBinding.instance.removeObserver(this)

PS. 講到iOS App Life Cycle 一定要秀一下這張經典圖

3. 對照表

iOS Android Flutter
init onCreate createState
viewDidLoad initState
viewWillAppear onStart 這個沒有真的滿傷的⚠️⚠️⚠️
viewDidLayoutSubviews build
viewDidAppear onResume
viewWillDisappear onPause
viewDidDisappear onStop
removeFromSuperview deactivate
deinit onDestroy dispose
WillEnterForeground onRestart
DidBecomeActive onStart resumed
WillResignActive onPause 這個沒有對金融業也滿傷的⚠️⚠️⚠️
DidEnterBackground onStop paused

參考連結


本集內容Android版請見:iOS Developer Learning Android. Lesson 04

下集預告:InheritedWidget


上一篇
iOS Developer Learning Flutter. Lesson18 API POST
下一篇
iOS Developer Learning Flutter. Lesson20 InheritedWidget
系列文
iOS Developer Learning Flutter30

尚未有邦友留言

立即登入留言