Flutter 生命周期研究與應用

Flutter 生命周期包括了組件的生命周期以及App的生命周期。

一、組件生命周期

一個flutter組件主要分為無狀態組件(StatelessWidget)和有狀態組件(StatefulWidget),無狀態組件只在創建的時候渲染一次,創建完成后不能重新渲染,而有狀態組件可以根據交互或數據變化等,進行多次渲染,本文組件生命周期專指有狀態組件的生命周期。
組件生命周期主要是:createState、initState、didChangeDependencies、reassemble、didUpdateWidget、build、deactivate、dispose這幾個函數,從函數名我們可以初步感知其作用,它們大致的流轉關系及作用如圖:


有狀態組件生命周期簡圖.png

下面以一個簡單的Demo驗證這個流程
1.構建一個簡單的MyApp:

import 'package:flutter/material.dart';
import 'package:flutterlifecycledemo/ui/home_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  /// 初始化state,只會在第一次構建組件時調用一次
  ///
  /// 在此期間可以執行State各變量的初始賦值
  /// 同時可以在此處調用網絡請求,服務器返回數據后通過setState刷新頁面,類似于iOS中的ViewDidLoad方法
  @override
  void initState() {
    super.initState();
    print('initState_MyApp');
  }
  /// 該組件所依賴的State發生變化時觸發,或者在initState(組件首次創建)后觸發
  ///
  /// 該組件所依賴的State指全局的State,例如語言和主題等
  /// 此方法會觸發build
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies_MyApp');
  }

  /// 在debug模式下,每次熱重載都會觸發該函數
  ///
  /// 主要是在開發階段在此增加一些debug代碼,檢查代碼問題
  @override
  void reassemble() {
    super.reassemble();
    print('reassemble_MyApp');
  }

  /// 組件重新構建(如熱重載),父組件發生build的情況下觸發
  ///
  /// 注意:
  /// 父組件發生build,本組件(子組件)該方法才會被調用,并且該方法調用后一定會再調用本組件中的build方法
  /// 并且父組件首次創建不會觸發
  @override
  void didUpdateWidget(MyApp oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget_HomePage');
  }

  /// 組件在被移除節點后被調用
  ///
  /// 如果該組件被移除節點,然后未被插入到其他節點時,則會繼續調用dispose永久移除
  @override
  void deactivate() {
    super.deactivate();
    print('deactivate_MyApp');
  }

  /// 永久移除組件,釋放組件資源
  @override
  void dispose() {
    super.dispose();
    print('dispose_MyApp');
  }

  /// 構建需要渲染的widget,會被多次調用(會被setState/didChangeDependencies/didUpdateWidget觸發)
  ///
  /// 只做返回Widget的相關邏輯
  @override
  Widget build(BuildContext context) {
    print('build_MyApp');
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Color(0xff409eff),
        brightness: Brightness.light, // 默認狀態欄顏色
      ),
      home: HomePage(),
    );
  }
}

2.HomPage實現:


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

class HomePage extends StatefulWidget {
  HomePage();

  /// 創建state
  ///
  /// 當StatefulWidget被調用時會立即執行
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool showProgressHud = true;
  double cw = 60;
  /// 模擬加載數據
  void loadData(){
    // 5秒后調用setState
    Future.delayed(Duration(seconds: 5), (){
      setState(() {
        showProgressHud = false;
      });
    });
  }

  /// 初始化state,只會在第一次構建組件時調用一次
  ///
  /// 在此期間可以執行State各變量的初始賦值
  /// 同時可以在此處調用網絡請求,服務器返回數據后通過setState刷新頁面,類似于iOS中的ViewDidLoad方法
  @override
  void initState() {
    super.initState();
    print('initState_HomePage');
    loadData();
  }
  /// 該組件所依賴的State發生變化時觸發,或者在initState(組件首次創建)后觸發
  ///
  /// 該組件所依賴的State指全局的State,例如語言和主題等
  /// 此方法會觸發build
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies_HomePage');
  }

  /// 在debug模式下,每次熱重載都會觸發該函數
  ///
  /// 主要是在開發階段在此增加一些debug代碼,檢查代碼問題
  @override
  void reassemble() {
    super.reassemble();
    print('reassemble_HomePage');
  }

  /// 組件重新構建(如熱重載),父組件發生build的情況下觸發
  ///
  /// 注意:
  /// 父組件發生build,本組件(子組件)該方法才會被調用,并且該方法調用后一定會再調用本組件中的build方法
  /// 并且父組件首次創建不會觸發
  @override
  void didUpdateWidget(HomePage oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget_HomePage');
  }

  /// 組件在被移除節點后被調用
  ///
  /// 如果該組件被移除節點,然后未被插入到其他節點時,則會繼續調用dispose永久移除
  @override
  void deactivate() {
    super.deactivate();
    print('deactivate_HomePage');
  }

  /// 永久移除組件,釋放組件資源
  @override
  void dispose() {
    super.dispose();
    print('dispose_HomePage');
  }

  /// 構建需要渲染的widget,會被多次調用(會被setState/didChangeDependencies/didUpdateWidget觸發)
  ///
  /// 只做返回Widget的相關邏輯
  @override
  Widget build(BuildContext context) {
    print('build_HomePage');
    return Scaffold(
      appBar: AppBar(
        title: Text('Hello Flutter',style: TextStyle(color: Color(0xffffffff),fontSize: 16, fontWeight: FontWeight.bold),),
        centerTitle: true,
        actions: <Widget>[
          FlatButton( // 觸發setState,showProgressHud=true,當前HomeConent組件會被移除
            child: Text('loadData',style: TextStyle(color:  Color(0xffffffff),fontSize: 14),),
            onPressed: (){
              setState(() {
                showProgressHud = true;
                loadData();
              });
            },
          ),
          FlatButton( // 增加cw值,并只觸發setState,showProgressHud不變,當前SubHomePage組件不會被移除
            child: Text('click',style: TextStyle(color:  Color(0xffffffff),fontSize: 14),),
            onPressed: (){
              setState(() {
                cw += 10;
              });
            },
          ),
        ],
      ),
      body: Container(
        decoration: BoxDecoration(
          color: Color(0xffffffff),
        ),
        child: Center(
          child: showProgressHud ?
              CircularProgressIndicator() :
              Column(
                mainAxisAlignment: MainAxisAlignment.center ,
                children: <Widget>[
                  Container(width: cw, height: 44,color: Color(0xff889eff),),
                  SubHomePage()
                ],
              )

          ,
        ),
      ),
    );
  }
}

3.SubHomePage實現:


class SubHomePage extends StatefulWidget {
  @override
  _SubHomePageState createState() => _SubHomePageState();
}

class _SubHomePageState extends State<SubHomePage> {

  /// 初始化state,只會在第一次構建組件時調用一次
  ///
  /// 在此期間可以執行State各變量的初始賦值
  /// 同時可以在此處調用網絡請求,服務器返回數據后通過setState刷新頁面,類似于iOS中的ViewDidLoad方法
  @override
  void initState() {
    super.initState();
    print('initState_SubHomePage');
  }
  /// 該組件所依賴的State發生變化時觸發,或者在initState(組件首次創建)后觸發
  ///
  /// 該組件所依賴的State指全局的State,例如語言和主題等
  /// 子類很少重寫這個方法,非必要情況不用重寫此方法
  /// 此方法會觸發build
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies_SubHomePage');
  }

  /// 在debug模式下,每次熱重載都會觸發該函數
  ///
  /// 主要是在開發階段在此增加一些debug代碼,檢查代碼問題
  @override
  void reassemble() {
    super.reassemble();
    print('reassemble_SubHomePage');
  }

  /// 組件重新構建(如熱重載),父組件發生build的情況下觸發
  ///
  /// 注意:
  /// 父組件發生build,本組件(子組件)該方法才會被調用,并且該方法調用后一定會再調用本組件中的build方法
  /// 并且父組件首次創建不會觸發
  @override
  void didUpdateWidget(SubHomePage oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget_SubHomePage');
  }

  /// 組件在被移除節點后被調用
  ///
  /// 如果該組件被移除節點,然后未被插入到其他節點時,則會繼續調用dispose永久移除
  @override
  void deactivate() {
    super.deactivate();
    print('deactivate_SubHomePage');
  }

  /// 永久移除組件,釋放組件資源
  @override
  void dispose() {
    super.dispose();
    print('dispose_SubHomePage');
  }

  /// 構建需要渲染的widget,會被多次調用(會被setState/didChangeDependencies/didUpdateWidget觸發)
  ///
  /// 只做返回Widget的相關邏輯
  @override
  Widget build(BuildContext context) {
    print('build_SubHomePage');
    return Column(
      children: <Widget>[
        Container(height: 100,),
        Text('Welcome to flutter', style: TextStyle(color: Color(0xff333333), fontSize: 16),),
        Container(
          height: 10,
        ),
        FlatButton(
          onPressed: increaseAction,
          child: Text(
            'Increase',
            style: TextStyle(color: Color(0xffffffff), fontSize: 14),
          ),
          color: Color(0xff409eff),
        )
      ],
    );
  }

  void increaseAction(){
    setState(() {
    });
  }
}

頁面效果:


IMG_1001CAC5603D-1.jpeg

首次加載控制臺輸出:


截屏2020-06-28 上午3.13.33.png

正好驗證了首次加載有狀態組件的生命周期,其中build_HomePage打印了兩次,是因為HomePage在initState的時候調用了模擬網絡請求方法loadData,在請求完畢后調用了setState,從而觸發了HomePage的build。
接著點擊導航欄上的loadData(屬于HomePage的組件)按鈕,觸發:

FlatButton( // 觸發setState,showProgressHud=true,當前SubHomePage組件會被移除
            child: Text('loadData',style: TextStyle(color:  Color(0xffffffff),fontSize: 14),),
            onPressed: (){
              setState(() {
                showProgressHud = true;
                loadData();
              });
            },
          ),

此時控制臺輸出:


截屏2020-06-28 上午3.26.36.png

我們發現首次生成的SubHomePage被移除了,并且loadData完成后生成新的SubHomePage,新的SubHomePage會接著走新的首次加載流程

child: Center(
          child: showProgressHud ?
              CircularProgressIndicator() :
              Column(
                mainAxisAlignment: MainAxisAlignment.center ,
                children: <Widget>[
                  Container(width: cw, height: 44,color: Color(0xff889eff),),
                  SubHomePage()
                ],
              )

          ,
        ),

此時我們點HomePage導航欄上的click按鈕 ,增加矩形的寬度:

FlatButton( // 增加cw值,并只觸發setState,showProgressHud不變,當前SubHomePage組件不會被移除
            child: Text('click',style: TextStyle(color:  Color(0xffffffff),fontSize: 14),),
            onPressed: (){
              setState(() {
                cw += 10;
              });
            },
          ),

控制臺輸出:


截屏2020-06-28 上午3.36.49.png

此時我們可以看到HomePage的build觸發了其子組件SubHomePage的didUpdateWidget和build方法,但是SubHomePage仍然是原來的SubHomePage,并未重新釋放(如果釋放后重構會調用dispose和initState),只是重新build了一次。
點擊SubHomePage上的increase,控制臺輸出:


截屏2020-06-28 上午3.42.26.png

cmd+\ 熱重載,控制臺輸出:
截屏2020-06-28 下午12.21.37.png
二、App生命周期

app生命周期比較簡單,具體可以參考官方文檔

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375