首先在介紹flutter之前凡伊,我們需要了解一個(gè)概念,在dart中的理念是一切皆為對(duì)象窒舟,而在flutter上系忙,我們的理念是一切皆widget(可以理解為組件),flutter就是谷歌封裝的完善的基于MD風(fēng)格以及ios的Cupertino風(fēng)格的基于dart語(yǔ)言的一套u(yù)i組件框架惠豺,由于dart作為谷歌新系統(tǒng)的官方指定語(yǔ)言银还,并且為ios和安卓統(tǒng)一了開(kāi)發(fā)風(fēng)格,所以我們可以用flutter開(kāi)發(fā)出跨平臺(tái)的app洁墙,現(xiàn)在我們從widget開(kāi)始介紹flutter的組件
widget組件
在flutter中存在兩種widget蛹疯,一種是存在狀態(tài)改變的StatefulWidget 和無(wú)狀態(tài)改變的StatelessWidget ,這里的狀態(tài)是否需要改變對(duì)應(yīng)著我們開(kāi)發(fā)的過(guò)程中這個(gè)組件是否需要進(jìn)行動(dòng)態(tài)的ui更改操作扫俺,如果說(shuō)我們需要更改ui苍苞,這時(shí)候就需要繼承StatefulWidget 組件了,否則頁(yè)面就是個(gè)靜態(tài)的ui無(wú)法進(jìn)行更改,當(dāng)然在開(kāi)發(fā)的過(guò)程中羹呵,如果確定當(dāng)前頁(yè)面是靜態(tài)的骂际,推薦繼承StatelessWidget 組件,因?yàn)閷?duì)于flutter而言冈欢,靜態(tài)的頁(yè)面渲染的速度比動(dòng)態(tài)的組件要快一些歉铝,并且由于是靜態(tài)ui,渲染一次以后就不會(huì)進(jìn)行更改重新渲染凑耻,所以資源的消耗也會(huì)更小一些(當(dāng)然太示,如果怕出意外或者頻繁改動(dòng),所有的組件都繼承StatefulWidget 開(kāi)發(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(
);
}
}
從上面可以看出來(lái)邻吭,兩個(gè)widget都有build方法餐弱,這個(gè)build方法就是組件加載ui的方法,我們需要如何布局囱晴,組合出當(dāng)前的組件就需要在build方法中開(kāi)發(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開(kāi)發(fā)經(jīng)驗(yàn)的應(yīng)該會(huì)發(fā)現(xiàn)這點(diǎn)flutter和React很相似管嬉,React也是靠綁定組件的狀態(tài),修改狀態(tài)來(lái)刷新ui的朗鸠,所以有經(jīng)驗(yàn)的童鞋蚯撩,可以按照React和原生安卓開(kāi)發(fā)的經(jīng)驗(yàn)來(lái)理解flutter的生命周期,會(huì)事半功倍)烛占,所以接下來(lái)我們學(xué)習(xí)一下widget的生命周期
從上面可以看出來(lái)widget的生命周期大概分為三個(gè)階段
初始化(插入渲染樹(shù))
狀態(tài)改變(在渲染樹(shù)中存在)
銷毀(從渲染樹(shù)種移除)
初始化階段
構(gòu)造函數(shù)
構(gòu)造函數(shù)屬于每個(gè)類的入口胎挎,肯定是widget的第一個(gè)方法沟启,一般我們都認(rèn)
為構(gòu)造函數(shù)不屬于生命周期的方法,只認(rèn)為是觸發(fā)生命周期的入口方法
initState
這個(gè)方法是widget的初始化方法犹菇,我們可以理解為原生安卓開(kāi)發(fā)的時(shí)候Activity的onCreate生命周期德迹,這
個(gè)方法在組件創(chuàng)建的時(shí)候只會(huì)觸發(fā)一次,我們可以在這個(gè)方法中調(diào)用一些參數(shù)的初始化操作揭芍,以及控制器的
一些監(jiān)聽(tīng)等胳搞,和我們?cè)_(kāi)發(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有什么作用或者說(shuō)具體的場(chǎng)景是什么呢?
我們舉一個(gè)例子:假設(shè)我們有一個(gè)需求页衙,頁(yè)面上需要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傳遞上下文的操作
...
}
接下來(lái)我們看看這個(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è)生命周期我們一開(kāi)始也介紹了,用來(lái)掛載組件進(jìn)行最終ui布局渲染使用的两踏,但是由于我們可能存在ui
修改的情況授段,也就是說(shuō),這個(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)建出來(lái)
新的widget,然后和舊的狀態(tài)下的widget進(jìn)行比較器仗,看看有什么屬性不一樣融涣,有什么進(jìn)行了改變,然后重新
綁定,這個(gè)函數(shù)有個(gè)比較坑的點(diǎn)威鹿,比如我們改動(dòng)了以后妓盲,可能監(jiān)聽(tīng)的函數(shù)改變了,或者控制器變更了专普,我們
必須要在當(dāng)前方法進(jìn)行移除舊的控制器和監(jiān)聽(tīng)事件悯衬,然后重新綁定新的,否則會(huì)影響組件運(yùn)行檀夹,我們通過(guò)上
面的生命周期圖可以看出來(lái)筋粗,當(dāng)前函數(shù)調(diào)用完畢以后,就會(huì)再次調(diào)用build方法炸渡,也就是重新創(chuàng)建組件布局娜亿,
所以我們也可以確定每次我們修改完?duì)顟B(tài)后,flutter是重新創(chuàng)建widget出來(lái)
組件銷毀階段
deactivate
這個(gè)是在銷毀之前調(diào)用的生命周期蚌堵,目前具體的作用博主也沒(méi)使用過(guò)买决,不過(guò)經(jīng)過(guò)測(cè)試,當(dāng)前函數(shù)是在組件
還處于可見(jiàn)狀態(tài)下調(diào)用的吼畏,在dispose之前調(diào)用
dispose
組件調(diào)用到當(dāng)前函數(shù)的時(shí)候督赤,就會(huì)觸發(fā)真的移除組件,銷毀對(duì)象泻蚊,移除控制器躲舌,取消監(jiān)聽(tīng)事件等操作,
不過(guò)執(zhí)行當(dāng)前函數(shù)代表正在銷毀性雄,可以理解為還沒(méi)銷毀完畢没卸,當(dāng)前函數(shù)執(zhí)行完畢以后就是真的銷毀調(diào)用完畢
好了,經(jīng)過(guò)上面的生命周期講解秒旋,可能大概知道了widget的生命周期以及大概的作用约计,那么肯定有人會(huì)問(wèn),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ù)說(shuō)新的sdk可能會(huì)改動(dòng)沽瘦,博主目前沒(méi)測(cè)試出來(lái))
其他特殊情況
注(博主在使用的過(guò)程中也遇到了很多其他的情況革骨,比如):
1.dispose 生命周期可能不會(huì)調(diào)用,在調(diào)用完deactivate 周期以后就掛載了新的節(jié)點(diǎn)到組件樹(shù)中的情況析恋。
2.didChangeDependencies生命周期一般情況下只有創(chuàng)建的時(shí)候會(huì)調(diào)用良哲,起初博主以為整個(gè)創(chuàng)建的周期的生命周期
都是只會(huì)調(diào)用一次,但是后來(lái)博主發(fā)現(xiàn)觸發(fā)了組件依賴的InheritedWidget改變的時(shí)候助隧,貌似也會(huì)觸發(fā)這個(gè)
生命周期筑凫,具體的原理應(yīng)該就是我們之前看BuildContext.inheritFromWidgetOfExactType有關(guān)。