淄博網(wǎng)站制作seo優(yōu)化專員
狀態(tài)管理必要性
Flutter基于聲明式構(gòu)建UI,原生則是命令式,狀態(tài)管理是用于解決聲明式開發(fā)帶來的問題。
例:命令式的原生,數(shù)據(jù)更新需要拿到對(duì)應(yīng)控件并更改其顯示值;而聲明式則需要更改數(shù)據(jù)值并通過setstate更新狀態(tài),重新構(gòu)建組件
Flutter 中有這么一種說法: UI = f(state):
聲明式的優(yōu)勢(shì)
-
優(yōu)勢(shì):
-
無需繁瑣地控制組件,只需聚焦于狀態(tài)管理,負(fù)責(zé)狀態(tài)—>UI的映射
-
劣勢(shì):
-
邏輯和頁面UI耦合,導(dǎo)致無法復(fù)用/單元測(cè)試、修改混亂等:MVVM等架構(gòu)解決
-
跨頁面訪問數(shù)據(jù)
-
控制頁面刷新范圍
provider工作原理
provider內(nèi)部為DelegateWidget(委托組件)是一個(gè)StatefulWidget,可更新,具有生命周期,借助各種代理完成
狀態(tài)共享使用InheritedProvider這個(gè)InheritedWidget實(shí)現(xiàn)
通過MultiProvider和Consumer封裝,對(duì)組合與刷新顆粒度控制
provider工作流程:
設(shè)置到changeNotifierProvider的changeNotifier被執(zhí)行addListener添加監(jiān)聽listener
listener內(nèi)會(huì)調(diào)用StateDelegate的StateSetter方法,從而調(diào)用到StatefulWidget的setState
在changeNotifier執(zhí)行notifyListeners時(shí),最終觸發(fā)setState更新
provider異同
- ListenableProvider / ChangeNotifierProvider
ListenableProvider提供的對(duì)象是繼承了Listenable抽象類的子類,只能通過繼承來實(shí)現(xiàn)addListener/removeListener方法,手動(dòng)管理收聽者
changeNotifier實(shí)現(xiàn)了Listenable,而混入了changeNotifier的類自動(dòng)實(shí)現(xiàn)了監(jiān)聽管理
ChangeNotifierProvider 和 ListenableProvider 究竟區(qū)別在哪呢,ChangeNotifierProvider 會(huì)在你需要的時(shí)候,自動(dòng)調(diào)用其 _disposer 方法。
-
ValueListenableProvider,提供了繼承/混入/實(shí)現(xiàn)了ValueListenable的model,專門用于只有一個(gè)單一變化數(shù)據(jù)的ChangeNotifier,通過ValueListenable處理的類不再需要數(shù)據(jù)更新時(shí)調(diào)用notifyListeners。
-
StreamProvider,專門提供一條Single Stream,提供了方法捕獲異常、更新數(shù)據(jù)、構(gòu)建流、構(gòu)建流控制器等
狀態(tài)同步
- 獲取頂層數(shù)據(jù):flutter在每個(gè)element上維護(hù)一個(gè)InheritedWidget哈希表來向下傳遞element樹中的信息,通常情況下,多個(gè)element引用相同的哈希表,并且該表僅在element引入新的InheritedWidget時(shí)改變, 時(shí)間復(fù)雜度為O(1)。
- 通知刷新:listener模式,model中維護(hù)聽眾,并通過notifiedListener通知刷新,全局狀態(tài)需放在頂層之上,優(yōu)先初始化
數(shù)據(jù)初始化
- 全局?jǐn)?shù)據(jù):main方法執(zhí)行,保證只執(zhí)行一次
- 單頁面數(shù)據(jù):StatefulWidget中的InitState中不可執(zhí)行Provider.of(context),當(dāng)監(jiān)聽后,在notifyListeners的時(shí)候,會(huì)觸發(fā)context所對(duì)應(yīng)的State的[State.build]和[State.didChangeDependencies]方法,數(shù)據(jù)到來時(shí)又會(huì)觸發(fā)下一次請(qǐng)求,無限請(qǐng)求下去。
解決頁面和邏輯的耦合
思路:
- 通過flutter樹機(jī)制解決,如provider
- 通過依賴注入,如Get
通過flutter樹機(jī)制處理V—>P的獲取
flutter三棵樹:widget、element、render object
widget樹是虛擬結(jié)構(gòu),只是描述組件嵌套關(guān)系,但element和renderObject在運(yùn)行時(shí)實(shí)際存在。element組件中包含了_parent屬性,存放其父節(jié)點(diǎn)element,而其又實(shí)現(xiàn)了buildContext接口,包含了對(duì)樹結(jié)構(gòu)操作的方法
原本應(yīng)該是通過context.findAncestorStateOfType向上獲取父組件的信息,在有了provider之后通過provider.of(context)向上獲取頂層provider組件中的presenter對(duì)象
通過依賴注入解決V—>P的獲取
擺脫context依賴,基于get借助一個(gè)全局單例的map存儲(chǔ)對(duì)象,通過依賴注入的方式,實(shí)現(xiàn)對(duì)Presenter層的獲取,使得可在任意類中獲取到Presenter
map對(duì)應(yīng)的key是runtimeType+tag,其中tag為可選參數(shù),value對(duì)應(yīng)object
get也可解決跨頁面訪問數(shù)據(jù)
避免setstate全局更新
觀察者模式,局部更新
- ValueNotifier、ValueListenableBuilder
- ChangeNotifierProvider、ChangeNotifier、Consumer:從頂層ChangeNotifierProvider獲取存儲(chǔ)的ChangeNotifier,Consumer作為子組件獲取對(duì)應(yīng)數(shù)據(jù)
Get對(duì)應(yīng)方式則是Get.put和GetBuilder,Get.put提前存儲(chǔ)數(shù)據(jù)對(duì)象,為GetBuilder組件指定數(shù)據(jù)類型作為泛型,因?yàn)镚et基于單例,所以GetBuilder可以直接通過泛型獲取到存入的對(duì)象,在builder方法中暴露,使得組件和數(shù)據(jù)建立了監(jiān)聽關(guān)系,并在數(shù)據(jù)更新后只驅(qū)動(dòng)將其作為泛型的GetBuilder組件更新
使用缺陷
- provider的context層級(jí)過高,如provider傳入的context是根層級(jí)的,而provider在element樹中是根層級(jí)下面
解決:對(duì)應(yīng)組件外嵌套一層builder,拿到該結(jié)點(diǎn)對(duì)應(yīng)的context / provider作為根結(jié)點(diǎn)
- Get全局單例
Get全局單例默認(rèn)以runtimeType為key進(jìn)行對(duì)象存儲(chǔ),而不同詳情頁實(shí)例對(duì)應(yīng)的是同一個(gè)class,key值一樣,不添加tag參數(shù),在Get.find時(shí)會(huì)獲取到已經(jīng)存儲(chǔ)的對(duì)象,即數(shù)據(jù)混淆了。
Get存儲(chǔ)的對(duì)象也得回收,dipose時(shí)進(jìn)行delete或者使用Get中提供的組件,如GetBuilder,會(huì)在dispose中釋放