Flutter中State的生命周期

Flutter中State的生命周期

Flutter中State類(狀態(tài))一般與StatefulWidget配合使用间护,來實(shí)現(xiàn)Widget的更新。State負(fù)責(zé)維護(hù)StatefulWidget的狀態(tài)又兵,并保存其狀態(tài)信息。當(dāng)Widget的狀態(tài)發(fā)生變化時卒废,用戶只需要調(diào)用setState()方法沛厨,F(xiàn)ultter引擎便會重新構(gòu)建Widget樹來更新UI。其中setState()方法更類似于一個消息機(jī)制摔认,用于通知Flutter引擎Widget狀態(tài)發(fā)生了變化逆皮,F(xiàn)lutter引擎收到通知后便會更新Widget樹。

由于State管理的StatefulWidget的狀態(tài)信息级野,并負(fù)責(zé)了StateWidget控件的更新页屠,因此了解其的生命周期對于Flutter開發(fā)具有重要意義。在某些應(yīng)用場景下我們需要知道State和Widget何時被構(gòu)建蓖柔,何時被銷毀以及何時被更新等辰企。

1. State簡介

State類的接口如以下代碼所示,其中我們開發(fā)中最常用的為build()和setState()方法,分別用于構(gòu)建Widget和通知Flutter引擎更新Widget樹况鸣。State類持有了一個Widget對象牢贸,通過該對象來重構(gòu)Widget樹,同時持有一個Widget的一個上下文镐捧,來獲取該Widget在Widget樹中的位置潜索。

//State為一個模板虛類,必須繼承才能夠使用
@optionalTypeArgs
abstract class State<T extends StatefulWidget> with Diagnosticable {
  T? _widget;  //State的實(shí)例中會持有一個Widget對象
  StatefulElement? _element;  //持有StatefulElement對象懂酱,構(gòu)建Element樹
  BuildContext get context {  //持有context對象竹习,實(shí)際上為StatefulElement
    return _element!;
  }
  
    Widget build(BuildContext context);  //構(gòu)建Widget樹
    
    void initState(){}  //初始化State
    
    void didUpdateWidget(covariant T oldWidget) { }  //更新Widget
    
    void reassemble() {}  //重新裝配Widget,一般在熱重載時調(diào)用
    
    void setState(VoidCallback fn) {}  //更新State列牺,后面詳細(xì)解釋
    
    void deactivate() {}  //注銷Widget整陌,將Widget從樹中移除,不釋放資源瞎领。
    
    void dispose() {}  //銷毀Widget泌辫,將Widget從樹中真正移除,并釋放資源
      
    void didChangeDependencies() {}  //更改依賴九默,State依賴發(fā)生變化
}

在開發(fā)中震放,我們一般只需要重載build()方法來定義子Widget,同時使用setState()方法來通知Flutter引擎更新UI驼修。其中build()方法完全由用戶定義的殿遂,這里我們給出setState()的主要代碼來探究setState()方法的運(yùn)行機(jī)制诈铛,代碼如下:

  //setState()方法的主要代碼,略去了調(diào)試信息
  @protected
  void setState(VoidCallback fn) {  //傳入一個回調(diào)函數(shù)fn
    final dynamic result = fn() as dynamic;
    _element!.markNeedsBuild();    //將Element標(biāo)記為需要重新build()
  }
  
  //owner為BuildOwner類型墨礁,同樣略去調(diào)試信息癌瘾。
  void markNeedsBuild() {
    owner!.scheduleBuildFor(this);  //傳入該Element,并進(jìn)入Flutter引擎的調(diào)度序列饵溅,等待重構(gòu)
  }

從setState()中的源碼可以看出,傳入的回調(diào)函數(shù)會被運(yùn)行妇萄,但是不影響Flutter引擎的build()過程蜕企,換句話說,即使回調(diào)函數(shù)fn()為空冠句,F(xiàn)lutter引擎也會去根據(jù)現(xiàn)有的值去更新UI轻掩。在代碼層面以下兩種書寫方式是等價的。

//書寫方式1:先更新UI值懦底,再調(diào)用setState()方法
TextButton(
  child: Text('$_counter'),
  onPressed:(){
    ++_counter;  //先更新需要變動的UI值
    setState(() {});  //再調(diào)用setState()方法
  },
);

//書寫方式2:在setState()方法體中更新UI值
TextButton(
  child: Text('$_counter'),
  onPressed:(){
    setState(() {
        ++_counter;  //在setState()方法中更新UI值
    });  
  },
);

但是為了代碼的可讀性唇牧,即讓程序員知道我們在什么地方刷新了UI,還是會將需要更新UI的值放在setState()的回調(diào)函數(shù)體中聚唐。

2.State()的生命周期

清楚地知道State()的生命周期對使用Flutter開發(fā)是很有意義的丐重。舉例來說,如果在某些情況下我們需要用到Widget中的參數(shù)來完成對State<Widget>的初始化操作杆查,我們就可以在initState()方法中通過value=widget.initValue的方式來進(jìn)行初始化扮惦。如果采用State<Widget>的構(gòu)造函數(shù)來進(jìn)行初始化的話,就會造成代碼的冗余和可讀性變差的問題亲桦。另外崖蜜,了解State何時被構(gòu)建何時被銷毀,對程序開發(fā)也具有重要的意義客峭。

在上一節(jié)豫领,我們以及較為詳細(xì)的介紹了State類中的成員,這里給出一個表格來詳細(xì)介紹State中方法的作用舔琅。

方法 作用
initState() 初始化State時調(diào)用等恐,將State插入渲染樹,只會調(diào)用一次
didChangeDependencies() State依賴對象變化時調(diào)用搏明,如改變語言和主題時會調(diào)用該方法鼠锈。
didUpdateWidget() 組件狀態(tài)改變時調(diào)用,可能調(diào)用多次
build() 構(gòu)建Widget
deactivate() 注銷星著,將State移除渲染樹购笆,但是暫時不銷毀
dispose() 銷毀,將State從內(nèi)存中移除
reassemble() 重組虚循,當(dāng)熱面熱加載時會被調(diào)用

下面我們將以一個簡單的頁面跳轉(zhuǎn)的代碼來探究Flutter的生命周期同欠。代碼由兩個頁面構(gòu)成样傍,分別為StateDemoPage1(頁面1)和StateDemoPage2(頁面2),他們都只有一個TextButton控件铺遂,StateDemoPage1實(shí)現(xiàn)了到StateDemoPage2的一個簡單跳轉(zhuǎn)衫哥,StateDemoPage2實(shí)現(xiàn)了一個點(diǎn)擊更換Button顏色的邏輯,通過這個簡單跳轉(zhuǎn)我們來觀察State()的生命周期襟锐,代碼如下:

  //頁面1代碼
  class StateDemoPage1 extends StatefulWidget{

  const StateDemoPage1({
    Key key
  });

  @override
  State<StatefulWidget> createState() {
    return new _StateDemoPage1();
  }
}

class _StateDemoPage1 extends State<StateDemoPage1>{
  @override
  Widget build(BuildContext context) {
    print("_StateDemoPage1: build(創(chuàng)建Widget,構(gòu)建Widget樹)");
    return Scaffold(
      body: Center(
        child: TextButton(
          child: Text('跳轉(zhuǎn)到下個頁面'),
          //點(diǎn)擊后計數(shù)器自增
          onPressed:(){
            Navigator.push(context, MaterialPageRoute(builder: (context){
              StateDemoPage2();
            }));
          },
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.resolveWith((states) => Colors.blue)
          )
        ),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    print("_StateDemoPage1: initState(初始化State)");
  }

  @override
  void didUpdateWidget(covariant StateDemoPage1 oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("_StateDemoPage1: didUpdateWidget(更新Widget)");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("_StateDemoPage1: deactive(注銷Widget撤逢,將Widget從樹中移除,不釋放資源粮坞。)");
  }

  @override
  void dispose() {
    super.dispose();
    print("_StateDemoPage1: dispose(銷毀Widget蚊荣,將Widget從樹中真正移除,并釋放資源)");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("_StateDemoPage1: reassemble(重組Widget)");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("_StateDemoPage1: didChangeDependencies(更改依賴莫杈,State依賴發(fā)生變化)");
  }

  _StateDemoPage1(){
    print("_StateDemoPage1: constructor(更改依賴互例,State依賴發(fā)生變化)");
  }
}
  
//頁面2代碼
  class StateDemoPage2 extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return new _StateDemoPage2();
  }
}

class _StateDemoPage2 extends State<StateDemoPage2>{
  Color mBackGroundColor = Colors.redAccent;
  @override
  Widget build(BuildContext context) {
    print("_StateDemoPage2: build(創(chuàng)建Widget,構(gòu)建Widget樹)");
    return Scaffold(
      body: Center(
        child: TextButton(
            child: Text('這里是第二個頁面',
            style: TextStyle(
              color: Colors.white
            ),),
            onPressed: (){
              setState(() {
                mBackGroundColor=Colors.green;
              });
            },
            style: ButtonStyle(
                backgroundColor: MaterialStateProperty.resolveWith((states) => mBackGroundColor)
            )
        ),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    print("_StateDemoPage2: initState(初始化State)");
  }

  @override
  void didUpdateWidget(covariant StateDemoPage2 oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("_StateDemoPage2: didUpdateWidget(更新Widget)");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("_StateDemoPage2: deactive(注銷Widget,將Widget從樹中移除筝闹,不釋放資源媳叨。)");
  }

  @override
  void dispose() {
    super.dispose();
    print("_StateDemoPage2: dispose(銷毀Widget,將Widget從樹中真正移除关顷,并釋放資源)");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("_StateDemoPage2: reassemble(重組Widget)");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("_StateDemoPage2: didChangeDependencies(更改依賴糊秆,State依賴發(fā)生變化)");
  }

  _StateDemoPage2(){
    print("_StateDemoPage2: constructor(構(gòu)造函數(shù))");
  }
}

step1: 點(diǎn)擊開始運(yùn)行

頁面1展示

頁面1中State的初始化過程: 當(dāng)運(yùn)行頁面1時,依次運(yùn)行了_StateDemoPage1的:構(gòu)造函數(shù)->initState()->didChangeDependencies()->build()议双。

_StateDemoPage1: constructor(構(gòu)造函數(shù))
_StateDemoPage1: initState(初始化State)
_StateDemoPage1: didChangeDependencies(更改依賴扩然,State依賴發(fā)生變化)
_StateDemoPage1: build(創(chuàng)建Widget,構(gòu)建Widget樹)

頁面1中State的熱重載過程: 當(dāng)我們對頁面1進(jìn)行熱重載的時候,運(yùn)行了_StateDemoPage1的:reassemble()->didUpdateWidget()->build()聋伦。

_StateDemoPage1: constructor(構(gòu)造函數(shù))
_StateDemoPage1: initState(初始化State)
_StateDemoPage1: didChangeDependencies(更改依賴夫偶,State依賴發(fā)生變化)
_StateDemoPage1: build(創(chuàng)建Widget,構(gòu)建Widget樹)
_StateDemoPage1: reassemble(重組Widget)
_StateDemoPage1: didUpdateWidget(更新Widget)
_StateDemoPage1: build(創(chuàng)建Widget,構(gòu)建Widget樹)

step2:在點(diǎn)擊頁面1中的跳轉(zhuǎn)按鈕后,跳轉(zhuǎn)到頁面2

頁面2展示

頁面2初始化過程:此時觉增,頁面2進(jìn)入了初始化過程兵拢,其構(gòu)建過程與頁面1相同。而頁面1為不可見狀態(tài)逾礁,但是被沒有進(jìn)入銷毀過程说铃。終端輸出如下:

_StateDemoPage2: constructor(構(gòu)造函數(shù))
_StateDemoPage2: initState(初始化State)
_StateDemoPage2: didChangeDependencies(更改依賴,State依賴發(fā)生變化)
_StateDemoPage2: build(創(chuàng)建Widget,構(gòu)建Widget樹)

step3:此時點(diǎn)擊頁面2嘹履,更換到綠色按鈕

image

頁面2的更新過程: 此時腻扇,調(diào)用了build()方法來更新了UI。

_StateDemoPage2: build(創(chuàng)建Widget,構(gòu)建Widget樹)

step4: TopBar上的點(diǎn)擊返回

頁面2的銷毀過程: 此時,我們進(jìn)入了銷毀過程,依次調(diào)用了_StateDemoPage2的deactive()->dispose()方法來完成頁面2的銷毀岂贩。

_StateDemoPage2: deactive(注銷Widget郁惜,將Widget從樹中移除,不釋放資源席噩。)
_StateDemoPage2: dispose(銷毀Widget蛾坯,將Widget從樹中真正移除丑搔,并釋放資源)

StatefulWidget的生命周期如下圖所示括荡,我們可以大致分為3個階段:

  • 初始化過程: constructor->initState()->didChangeDependencies()->build()
  • 更新過程: reassemble()->didUpdateWidget()
  • 銷毀過程: deactive()->dispose()
State的生命周期

歡迎關(guān)注

貓棗編程

參考文獻(xiàn)

[1] https://book.flutterchina.club/chapter3/

[2] https://blog.csdn.net/u011272795/article/details/82695920

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末高镐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子畸冲,更是在濱河造成了極大的恐慌嫉髓,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邑闲,死亡現(xiàn)場離奇詭異岩喷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)监憎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婶溯,“玉大人鲸阔,你說我怎么就攤上這事∑” “怎么了褐筛?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叙身。 經(jīng)常有香客問我渔扎,道長,這世上最難降的妖魔是什么信轿? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任晃痴,我火速辦了婚禮,結(jié)果婚禮上财忽,老公的妹妹穿的比我還像新娘倘核。我一直安慰自己,他們只是感情好即彪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布紧唱。 她就那樣靜靜地躺著,像睡著了一般隶校。 火紅的嫁衣襯著肌膚如雪漏益。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天深胳,我揣著相機(jī)與錄音绰疤,去河邊找鬼。 笑死舞终,一個胖子當(dāng)著我的面吹牛峦睡,可吹牛的內(nèi)容都是我干的翎苫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼榨了,長吁一口氣:“原來是場噩夢啊……” “哼煎谍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起龙屉,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呐粘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后转捕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體作岖,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年五芝,在試婚紗的時候發(fā)現(xiàn)自己被綠了痘儡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡枢步,死狀恐怖沉删,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情醉途,我是刑警寧澤矾瑰,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站隘擎,受9級特大地震影響殴穴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜货葬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一采幌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧震桶,春花似錦植榕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至淤堵,卻和暖如春寝衫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拐邪。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工慰毅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扎阶。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓汹胃,卻偏偏與公主長得像婶芭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子着饥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在flutter中犀农,一個statefulWidget會對應(yīng)一個state類,state表示對應(yīng)的statefulW...
    shfan閱讀 1,041評論 0 0
  • State 的生命周期宰掉,指的是在用戶參與的情況下呵哨,其關(guān)聯(lián)的 Widget 所經(jīng)歷的,從創(chuàng)建到顯示轨奄,再到更新最后到停...
    過愙2015閱讀 504評論 0 0
  • State 的生命周期孟害,指的是在用戶參與的情況下,其關(guān)聯(lián)的 Widget 所經(jīng)歷的挪拟,從創(chuàng)建到顯示挨务,再到更新最后到停...
    過愙2015閱讀 1,292評論 0 2
  • 前言 在上篇文章中,了解到通過父 Widget 初始化時傳入的靜態(tài)配置玉组,StatelessWidget 就能完全控...
    Eren丶耶格爾閱讀 5,242評論 1 5
  • 今天感恩節(jié)哎谎柄,感謝一直在我身邊的親朋好友。感恩相遇球切!感恩不離不棄。 中午開了第一次的黨會绒障,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,566評論 0 11