為什么 build 方法放在 State 中而不是在 StatefulWidget 中

老孟導(dǎo)讀:此篇文章是生命周期相關(guān)文章的番外篇,在查看源碼的過程中發(fā)現(xiàn)了這一有趣的問題,歡迎大家一起探討。
Flutter 中Stateful 組件的生命周期:http://laomengit.com/blog/20201227/Stateful%E7%BB%84%E4%BB%B6%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html
Flutter 中與平臺(tái)相關(guān)的生命周期:http://laomengit.com/blog/20201227/%E7%9B%B8%E5%85%B3%E5%B9%B3%E5%8F%B0%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html
博客中還有更多精彩文章,也歡迎加入 Flutter 交流群。
為什么 build 方法放在 State 中而不是在 StatefulWidget 中呢?其中前2點(diǎn)是源代碼的注釋中給出的原因,最后一點(diǎn)是我的一點(diǎn)個(gè)人理解。
靈活性
將 build 方法放在 State 中比放在 StatefulWidget 中更具靈活性,比如說,AnimatedWidget 是 StatefulWidget 的子類,AnimatedWidget 是一個(gè)抽象類,其中有一個(gè) Widget build(BuildContext context) 的抽象方法,此方法需要子類重寫,AnimatedWidget 源代碼如下:
abstract?class?AnimatedWidget?extends?StatefulWidget?{
??...
??///?Override?this?method?to?build?widgets?that?depend?on?the?state?of?the
??///?listenable?(e.g.,?the?current?value?of?the?animation).
??@protected
??Widget?build(BuildContext?context);
??///?Subclasses?typically?do?not?override?this?method.
??@override
??_AnimatedState?createState()?=>?_AnimatedState();
??...
}
刪除了一些代碼,保留了重點(diǎn)代碼。
試想一下,如果 build 方法放在 StatefulWidget 中,則 AnimatedWidget 中的 build 方法需要帶一個(gè) State 參數(shù),如下:
abstract?class?AnimatedWidget?extends?StatefulWidget?{
??...
??///?Override?this?method?to?build?widgets?that?depend?on?the?state?of?the
??///?listenable?(e.g.,?the?current?value?of?the?animation).
??@protected
??Widget?build(BuildContext?context,?AnimatedState?state);
??///?Subclasses?typically?do?not?override?this?method.
??@override
??_AnimatedState?createState()?=>?_AnimatedState();
??...
}
但 AnimatedState 是內(nèi)部實(shí)現(xiàn),并不需要開放給外部(開發(fā)者),外部也不需要知道 AnimatedState 的內(nèi)部實(shí)現(xiàn)。
閉包 this 指向異常
假設(shè) build 方法在 StatefulWidget 中,StatefulWidget 的子類寫法如下:
class?MyWidget?extends?StatefulWidget?{
??final?Color?color;
??@override
??Widget?build(BuildContext?context,?MyWidgetState?state)?{
????print('${this.color}');
????return?Container();
??}
}
此時(shí)的 this 指向的是 MyWidget 的實(shí)例,然后父組件改變顏色,重新構(gòu)建 MyWidget 組件,前一個(gè) MyWidget 的實(shí)例中的 this 依然指向前一個(gè) MyWidget 的實(shí)例,顏色并未發(fā)生變化。
如果 build 方法在 State 中,代碼如下:
class?MyWidget?extends?StatefulWidget?{
??final?Color?color;
??const?MyWidget({Key?key,?this.color})?:?super(key:?key);
??
??@override
??_MyWidgetState?createState()?=>?_MyWidgetState();
}
class?_MyWidgetState?extends?State<MyWidget>?{
??@override
??Widget?build(BuildContext?context)?{
????print('${widget.color}');
????return?Container();
??}
}
同樣,父組件改變顏色,重新構(gòu)建 MyWidget 組件,此時(shí)框架更新了 State 對(duì)象的 widget 屬性的引用,新的 MyWidget 實(shí)例和 $ {widget.color} 將顯示綠色。
性能
有狀態(tài)的組件包含StatefulWidget 和 State,當(dāng)有狀態(tài)組件的配置發(fā)生更改時(shí),StatefulWidget 將會(huì)被丟棄并重建,而 State 不會(huì)重建,框架會(huì)更新 State 對(duì)象中 widget 的引用,這極大的減輕了系統(tǒng)重建有狀態(tài)組件的工作。
此方式對(duì)動(dòng)畫來說極為重要,由于 State 不會(huì)被重建,保留了前面的狀態(tài),不斷的根據(jù)前一個(gè)狀態(tài)計(jì)算下一個(gè)狀態(tài)并重建其widget,達(dá)到動(dòng)畫的效果。

