為什么需要狀態(tài)管理岗仑?
剛開始構(gòu)建應(yīng)用的時候匹耕,只有少許狀態(tài)需要管理,這時候可能并不需要狀態(tài)管理荠雕。
但隨著功能增加稳其,會出現(xiàn)成百上千的狀態(tài),如果沒有狀態(tài)管理炸卑,就會亂成一鍋粥既鞠。
Provide是借助inheritWidget ,把共享狀態(tài)放到頂層materialApp之上,底層部件通過provider獲取該狀態(tài)盖文,并通過混合ChangeNotifier通知依賴于該狀態(tài)的組件刷新嘱蛋。Provide還提供了Provide.stream,讓我們能夠以處理流的方式處理數(shù)據(jù)。
開始吧洒敏!
這兩個頁面都同時依賴于counter 和 switcher兩個不同的狀態(tài)龄恋。并且一個頁面改變狀態(tài)之后另外一個頁面狀態(tài)也隨之改變。
該項目完整代碼已放在?Github
第一步:添加依賴
在pubspec.yaml中添加Provide的依賴桐玻。
provide : ^1.0.1? #這里版本查看官方
實際添加請參考:pub.dartlang.org/packages/pr…
由于版本沖突添加失敗請參考:?juejin.im/post/5b8958…?
import 'package:provide/provide.dart';
第二步篙挽,創(chuàng)建Model
這里實際上它承擔(dān)了State的職責(zé),但是為了和官方的State區(qū)分所以叫做model镊靴。?
這里以簡單的Counter為例:
對比Scoped_m這里我們可以看到铣卡,數(shù)據(jù)和操作數(shù)據(jù)的方法都在model中,我們可以很清晰的把業(yè)務(wù)分離出來偏竟。這里我們可以發(fā)現(xiàn)煮落,Provide模式中model不再需要繼承Model類,只需要實現(xiàn)Listenable踊谋,我們這里混入ChangeNotifier蝉仇,可以不用管理聽眾。
第三步:將狀態(tài)放入頂層?? 這里雖然定義在全局,但事實上也可以定義在頁面級
ProviderNode表示的是提供者
ProviderNode封裝了InheritWidget殖蚕,并且提供了 一個providers容器用于放置狀態(tài)轿衔。
ProviderScope 為Provider提供單獨的類型空間,它允許多個相同類型的提供者睦疫。默認使用ProviderScope('_default'),存放的時候你可以通過ProviderScope("name")來指定key害驹。
添加一組Provider的時候建議使用provideFrom或者provide方法,而不是provideAll蛤育,因為它可以檢查編譯時的類型錯誤宛官。?
Provider.value將counter包裝成了_ValueProvider。并在它的內(nèi)部提供了StreamController從而實現(xiàn)對數(shù)據(jù)進行流式操作瓦糕。?
第四步底洗,獲取狀態(tài)
一,同樣的Provide也提供了兩種獲取State的方法咕娄。我們先來介紹第一種亥揖,通過Provide小部件獲取。
每次通知數(shù)據(jù)刷新時圣勒,builder將會重新構(gòu)建這個小部件费变。builder方法接收三個參數(shù),這里主要介紹第二個和第三個灾而。第二個參數(shù)child:假如這個小部件足夠復(fù)雜胡控,內(nèi)部有一些小部件是不會改變的,那么我們可以將這部分小部件寫在Provide的child屬性中旁趟,讓builder不再重復(fù)創(chuàng)建這些小部件昼激,以提升性能庇绽。第三個參數(shù)counter:這個參數(shù)代表了我們獲取的頂層providers中的狀態(tài)。scope:通過指定ProviderScope獲取該鍵所對應(yīng)的狀態(tài)橙困。在需要使用多個相同類型狀態(tài)的時候使用瞧掺。
二,第二種獲取方式:Provide.value<T>(context)
這種方式實際上調(diào)用了context.inheritFromWidgetOfExactType找到頂層的_InheritedProviders來獲取到頂層providers中的狀態(tài)凡傅。
添加一個方法,用于獲取Counter實例:
Provide會在Counter發(fā)生變化的時候,觸發(fā)builder回調(diào)來更新界面
發(fā)通知
第五步:如何組織多個狀態(tài)
provide模式中你可以輕松組織多個狀態(tài)辟狈。只需要將狀態(tài)provide放進provider中就可以了。
第六步夏跷,獲取數(shù)據(jù)流
在將counter添加進providers的過程中進行了一次包裝哼转。我們剛才通過分析源碼知道了這個操作能夠讓我們處理流式數(shù)據(jù)。
通過 Provide.stream<T>(context) 就能獲取數(shù)據(jù)流槽华。需要注意的是壹蔓,這里每次獲取的數(shù)據(jù)流都如下:
不過在我的使用當中出現(xiàn)了streamTransformer失效的情況。在firstScreen和secondScreen同樣應(yīng)用這一段相同的代碼猫态,second screen的where方法能夠生效佣蓉,過濾掉奇數(shù)數(shù)據(jù),而first screen中則是收到了完整的數(shù)據(jù)亲雪。
需要注意的是勇凭,這里每次獲取的數(shù)據(jù)流都會重新創(chuàng)建一條新的流。
關(guān)于這個做法還有一些爭議义辕,具體可以查看這個issue:
不過這個功能還可以結(jié)合rxdart使用虾标,可以通過stream輕松構(gòu)建Observer,讓我們更加靈活的組織數(shù)據(jù)终息。
第七步夺巩,根據(jù)多個狀態(tài)重建小部件
當我們一個視圖可能依賴于多個狀態(tài)進行重建的時候贞让,可以使用ProvideMulti小部件周崭。
第八步,可能出現(xiàn)的問題
由于 Provide 自動將 Listenable 數(shù)據(jù)包裝并提供了 Provide.stream 接口喳张,讓我們可以通過監(jiān)聽這個流续镇,來獲取最新事件。但是當我們進行手動監(jiān)聽之后將會發(fā)生這件詭異的事情销部。
按理說這里應(yīng)該在數(shù)據(jù)發(fā)生變化的時候收到一條事件摸航,可是我們這里發(fā)現(xiàn)一次性輸出了 5 條flutter: Instance of 'Switcher'。
為什么是 5 條呢舅桩,這是因為我一共在 5 處 地方收聽過這個數(shù)據(jù)酱虎,包括使用 Provide Widget 也算一次收聽。
而當我退出第二個頁面之后再次進入擂涛,發(fā)現(xiàn)這次收到的數(shù)據(jù)比上次多了 5 條读串。
出現(xiàn)這個現(xiàn)象是由于這個 stream 是由工廠方法創(chuàng)建,每次調(diào)用 Provide.stream 都會重新創(chuàng)建出來一條流。就算收聽者不再收聽恢暖,這條流也會存在排监。
所以不要去手動監(jiān)聽你的 Provide.stream。
Stream模式
stream是使用Provide.stream<Counter>(context)獲取的
在provide中有一個概念叫scope,類的完整類名叫ProviderScope