(六)flutter入門之widget解惑

首先在介紹flutter之前,我們需要了解一個(gè)概念,在dart中的理念是一切皆為對(duì)象,而在flutter上,我們的理念是一切皆widget(可以理解為組件),flutter就是谷歌封裝的完善的基于MD風(fēng)格以及ios的Cupertino風(fēng)格的基于dart語言的一套u(yù)i組件框架,由于dart作為谷歌新系統(tǒng)的官方指定語言,并且為ios和安卓統(tǒng)一了開發(fā)風(fēng)格,所以我們可以用flutter開發(fā)出跨平臺(tái)的app,現(xiàn)在我們從widget開始介紹flutter的組件

widget組件

在flutter中存在兩種widget,一種是存在狀態(tài)改變的StatefulWidget 和無狀態(tài)改變的StatelessWidget ,這里的狀態(tài)是否需要改變對(duì)應(yīng)著我們開發(fā)的過程中這個(gè)組件是否需要進(jìn)行動(dòng)態(tài)的ui更改操作,如果說我們需要更改ui,這時(shí)候就需要繼承StatefulWidget 組件了,否則頁面就是個(gè)靜態(tài)的ui無法進(jìn)行更改,當(dāng)然在開發(fā)的過程中,如果確定當(dāng)前頁面是靜態(tài)的,推薦繼承StatelessWidget 組件,因?yàn)閷?duì)于flutter而言,靜態(tài)的頁面渲染的速度比動(dòng)態(tài)的組件要快一些,并且由于是靜態(tài)ui,渲染一次以后就不會(huì)進(jìn)行更改重新渲染,所以資源的消耗也會(huì)更小一些(當(dāng)然,如果怕出意外或者頻繁改動(dòng),所有的組件都繼承StatefulWidget 開發(fā),也是可以的,只是博主不推薦),現(xiàn)在我們創(chuàng)建一下這兩個(gè)不同的widget,看看到底有什么不同

StatelessWidget :

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
    );
  }
}

StatefulWidget :

class MyfulWidget extends StatefulWidget {
  @override
  _MyfulWidgetState createState() => _MyfulWidgetState();
}

class _MyfulWidgetState extends State<MyfulWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
    );
  }
}

從上面可以看出來,兩個(gè)widget都有build方法,這個(gè)build方法就是組件加載ui的方法,我們需要如何布局,組合出當(dāng)前的組件就需要在build方法中開發(fā),唯一的區(qū)別就是build所在的類不同,StatelessWidget 的build方法就在自己的類中,因?yàn)楫?dāng)前的組件是靜態(tài)組件,只要加載渲染一次即可,所以默認(rèn)指定了ui布局,就不需要其他操作了,但是StatefulWidget 的build是在createState的方法中創(chuàng)建的state子類中,這個(gè)state是flutter的概念,基本上所有的動(dòng)態(tài)的組件,谷歌的sdk中默認(rèn)都會(huì)指定對(duì)應(yīng)的State,而所有的ui加載也好,數(shù)據(jù)變動(dòng)也好都在這個(gè)子類中操作,那么我們的ui需要變動(dòng)怎么辦呢?這個(gè)時(shí)候就需要一個(gè)方法手動(dòng)觸發(fā)更改,刷新widget的生命周期(有React開發(fā)經(jīng)驗(yàn)的應(yīng)該會(huì)發(fā)現(xiàn)這點(diǎn)flutter和React很相似,React也是靠綁定組件的狀態(tài),修改狀態(tài)來刷新ui的,所以有經(jīng)驗(yàn)的童鞋,可以按照React和原生安卓開發(fā)的經(jīng)驗(yàn)來理解flutter的生命周期,會(huì)事半功倍),所以接下來我們學(xué)習(xí)一下widget的生命周期

1768723716-5b188ca549250_articlex.png

從上面可以看出來widget的生命周期大概分為三個(gè)階段

  • 初始化(插入渲染樹)

  • 狀態(tài)改變(在渲染樹中存在)

  • 銷毀(從渲染樹種移除)

初始化階段

構(gòu)造函數(shù)
構(gòu)造函數(shù)屬于每個(gè)類的入口,肯定是widget的第一個(gè)方法,一般我們都認(rèn)
為構(gòu)造函數(shù)不屬于生命周期的方法,只認(rèn)為是觸發(fā)生命周期的入口方法
initState
這個(gè)方法是widget的初始化方法,我們可以理解為原生安卓開發(fā)的時(shí)候Activity的onCreate生命周期,這
個(gè)方法在組件創(chuàng)建的時(shí)候只會(huì)觸發(fā)一次,我們可以在這個(gè)方法中調(diào)用一些參數(shù)的初始化操作,以及控制器的
一些監(jiān)聽等,和我們?cè)_發(fā)的習(xí)慣一樣,可以在這個(gè)方法中默認(rèn)initEvent()方法等其他操作,但是不建
議在這里做耗時(shí)操作,否則會(huì)影響到組件的加載創(chuàng)建,甚至可能導(dǎo)致崩潰
didChangeDependencies
這個(gè)函數(shù)會(huì)緊接著在init函數(shù)之后調(diào)用,并且可以調(diào)用并且可以調(diào)用BuildContext.inheritFromWidgetOfExactType,
可能很多人會(huì)疑惑這BuildContext.inheritFromWidgetOfExactType有什么作用或者說具體的場(chǎng)景是什么呢?
我們舉一個(gè)例子:假設(shè)我們有一個(gè)需求,頁面上需要tab切換操作,tab一般需要自定義一個(gè)TabController,
但是tab有兩種用法,還可以選擇使用默認(rèn)的DefaultTabController處理,這樣的話就不需要自定義控制器了,
在默認(rèn)的控制器中就用到了BuildContext.inheritFromWidgetOfExactType,我們大概看下源碼:
void didChangeDependencies() {
    super.didChangeDependencies();
    _updateTabController();//從這里調(diào)用了BuildContext.inheritFromWidgetOfExactType
    _initIndicatorPainter();
  }

接著我們看看_updateTabController方法:

void _updateTabController() {
    final TabController newController = widget.controller ?? DefaultTabController.of(context);//這里涉及了一個(gè)of傳遞上下文的操作
    ...
    }

接下來我們看看這個(gè)傳遞上下文的方法

static TabController of(BuildContext context) {
    final _TabControllerScope scope = context.inheritFromWidgetOfExactType(_TabControllerScope);//就是這里觸發(fā)了BuildContext.inheritFromWidgetOfExactType
    return scope?.controller;
  }

看到了大概的源碼實(shí)現(xiàn),我們大概了解了這個(gè)didChangeDependencies生命周期的作用,但是有人會(huì)疑惑,BuildContext.inheritFromWidgetOfExactType到底有什么作用呢?這里我的看法是,可以傳遞context使得組件之間可以跨組件獲取數(shù)據(jù)等操作(如果理解有誤或者有不同的看法,歡迎大佬指正)

build
這個(gè)生命周期我們一開始也介紹了,用來掛載組件進(jìn)行最終ui布局渲染使用的,但是由于我們可能存在ui
修改的情況,也就是說,這個(gè)函數(shù)不是只觸發(fā)一次的(是否觸發(fā)一次,是看是不是有state決定的,不是當(dāng)前
生命周期決定)

狀態(tài)改變階段

didUpdateWidget
該生命周期一般是我們組件出現(xiàn)了狀態(tài)改變的時(shí)候,就會(huì)觸發(fā)當(dāng)前函數(shù),在當(dāng)前函數(shù)中,flutter會(huì)創(chuàng)建出來
新的widget,然后和舊的狀態(tài)下的widget進(jìn)行比較,看看有什么屬性不一樣,有什么進(jìn)行了改變,然后重新
綁定,這個(gè)函數(shù)有個(gè)比較坑的點(diǎn),比如我們改動(dòng)了以后,可能監(jiān)聽的函數(shù)改變了,或者控制器變更了,我們
必須要在當(dāng)前方法進(jìn)行移除舊的控制器和監(jiān)聽事件,然后重新綁定新的,否則會(huì)影響組件運(yùn)行,我們通過上
面的生命周期圖可以看出來,當(dāng)前函數(shù)調(diào)用完畢以后,就會(huì)再次調(diào)用build方法,也就是重新創(chuàng)建組件布局,
所以我們也可以確定每次我們修改完?duì)顟B(tài)后,flutter是重新創(chuàng)建widget出來

組件銷毀階段

deactivate
這個(gè)是在銷毀之前調(diào)用的生命周期,目前具體的作用博主也沒使用過,不過經(jīng)過測(cè)試,當(dāng)前函數(shù)是在組件
還處于可見狀態(tài)下調(diào)用的,在dispose之前調(diào)用
dispose
組件調(diào)用到當(dāng)前函數(shù)的時(shí)候,就會(huì)觸發(fā)真的移除組件,銷毀對(duì)象,移除控制器,取消監(jiān)聽事件等操作,
不過執(zhí)行當(dāng)前函數(shù)代表正在銷毀,可以理解為還沒銷毀完畢,當(dāng)前函數(shù)執(zhí)行完畢以后就是真的銷毀調(diào)用完畢

好了,經(jīng)過上面的生命周期講解,可能大概知道了widget的生命周期以及大概的作用,那么肯定有人會(huì)問,widget怎么觸發(fā)狀態(tài)改變呢?在widget中存在setState函數(shù),在這個(gè)函數(shù)內(nèi)部可以編寫我們需要改變的時(shí)候觸發(fā)的業(yè)務(wù)代碼,當(dāng)此方法調(diào)用的時(shí)候,就代表著當(dāng)前的組件需要進(jìn)行狀態(tài)更改了,即會(huì)觸發(fā)組件狀態(tài)改變階段生命周期流程,但是這里我們需要注意的一點(diǎn)是,在flutter中,狀態(tài)改變的可能存在如下兩種

1.當(dāng)前組件內(nèi)部調(diào)用setState方法
2.當(dāng)前組件的父組件調(diào)用了setState方法,當(dāng)前的組件也會(huì)調(diào)用狀態(tài)改變重新渲染的流程,孩子組件調(diào)用
狀態(tài)改變的方法并不會(huì)觸發(fā)父組件的狀態(tài)改變的方法(據(jù)說新的sdk可能會(huì)改動(dòng),博主目前沒測(cè)試出來)

其他特殊情況

注(博主在使用的過程中也遇到了很多其他的情況,比如):
1.dispose 生命周期可能不會(huì)調(diào)用,在調(diào)用完deactivate 周期以后就掛載了新的節(jié)點(diǎn)到組件樹中的情況。
2.didChangeDependencies生命周期一般情況下只有創(chuàng)建的時(shí)候會(huì)調(diào)用,起初博主以為整個(gè)創(chuàng)建的周期的生命周期
都是只會(huì)調(diào)用一次,但是后來博主發(fā)現(xiàn)觸發(fā)了組件依賴的InheritedWidget改變的時(shí)候,貌似也會(huì)觸發(fā)這個(gè)
生命周期,具體的原理應(yīng)該就是我們之前看BuildContext.inheritFromWidgetOfExactType有關(guān)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容