Flutter2.*_狀態(tài)共享
????提及狀態(tài)管理,以我們之前介紹的觀察者模式來理解,可以輕松一些,特別是對應(yīng)flutter的eventbus,可以實現(xiàn)跨組件狀態(tài)同步,狀態(tài)的持有方進(jìn)行更新,然后發(fā)布狀態(tài),使用者們監(jiān)聽狀態(tài)改變事件,完成一些后續(xù)操作。
????對于狀態(tài)共享,存在一個原則:當(dāng)狀態(tài)是組件私有的,則這個狀態(tài)應(yīng)該由該組件管理。當(dāng)狀態(tài)需要跨組件共享時,那么這個狀態(tài)應(yīng)該涉及到的跨組件范圍內(nèi)的所有組件的父類來管理,對于跨組件的狀態(tài)管理,管理方式有很多,前面我們聊到的eventbus便是其中之一。
????當(dāng)然使用軟件設(shè)計模式里的觀察者可以解決我們的處理問題訴求,但是這種設(shè)計模式是用來管理狀態(tài)的最完美的解么?不是的,使用觀察者模式時,需要我們每次定義相關(guān)事件,然后在widgewt進(jìn)行初始時,將事件進(jìn)行注冊,相同的在注銷時要把所有訂閱事件進(jìn)行事件解綁,這當(dāng)然不是最優(yōu)的選擇,那么誰才是呢?如果了解之前我們的更新帖子,想必大家會有一個關(guān)于使用InheritedWidget的大膽想法,沒錯,就是依據(jù)InheritedWidget的處理思路,flutter提供了provider,即flutetr的跨組件狀態(tài)管理解決方案!
自定義Provider
??
????定義保存共享數(shù)據(jù)的InheritedWidget,由于不同業(yè)務(wù)的數(shù)據(jù)類型不能預(yù)期,所以使用范型接收:
//?一個通用的InheritedWidget,保存需要跨組件共享的狀態(tài)class?InheritedProvider?extends?InheritedWidget?{ ??InheritedProvider({ ????required?this.data, ????required?Widget?child, ??})?:?super(child:?child); ??final?T?data; ??@override ??bool?updateShouldNotify(InheritedProvider ?old)?{ ????//在此簡單返回true,則每次更新都會調(diào)用依賴其的子孫節(jié)點的`didChangeDependencies`。 ????return?true; ??}}
????數(shù)據(jù)保存已經(jīng)完成,接下來需要在數(shù)據(jù)變化時對InheritedProvider進(jìn)行重構(gòu),那么要處理的問題有兩個:
????·數(shù)據(jù)變化時如何通知;
????·重構(gòu)InheritedProvider的處理由誰來進(jìn)行;
????數(shù)據(jù)變化時的通知我們可以使用eventbus來進(jìn)行,對應(yīng)于flutter的sdk工具類ChangeNotifier,ChangeNotifier繼承自 Listenable,實現(xiàn)來發(fā)布-訂閱模式,定義結(jié)構(gòu)為:
class?ChangeNotifier?implements?Listenable?{
??List?listeners=[];
??@override
??void?addListener(VoidCallback?listener)?{
?????//添加監(jiān)聽器
?????listeners.add(listener);
??}
??@override
??void?removeListener(VoidCallback?listener)?{
????//移除監(jiān)聽器
????listeners.remove(listener);
??}
??
??void?notifyListeners()?{
????//通知所有監(jiān)聽器,觸發(fā)監(jiān)聽器回調(diào)?
????listeners.forEach((item)=>item());
??}
???
??...?//省略無關(guān)代碼}????此時通過Notifier的add*和remove*可以添加移除監(jiān)聽的訂閱者,使用notifyListeners()可以觸發(fā)所有的監(jiān)聽起回調(diào),此時數(shù)據(jù)通知的處理已經(jīng)完成,那么重構(gòu)工作呢?我們可以將要共享的狀態(tài)放入一個model類中,讓其繼承自changenotifier,當(dāng)共享的狀態(tài)發(fā)生變化時,只需調(diào)用notifyListeners方法即可通知訂閱者們重新構(gòu)建InheritedProvider,來看一下訂閱者類的實現(xiàn)代碼:
class?ChangeNotifierProvider?extends?StatefulWidget?{ ??ChangeNotifierProvider({ ????Key??key, ????this.data, ????this.child, ??}); ??final?Widget?child; ??final?T?data; ??//定義一個便捷方法,方便子樹中的widget獲取共享數(shù)據(jù) ??static?T?of (BuildContext?context)?{ ????final?type?=?_typeOf >(); ????final?provider?=??context.dependOnInheritedWidgetOfExactType >(); ????return?provider.data; ??} ??@override ??_ChangeNotifierProviderState ?createState()?=>?_ChangeNotifierProviderState ();}
class?_ChangeNotifierProviderState?extends?State >?{ ??void?update()?{ ????//如果數(shù)據(jù)發(fā)生變化(model類調(diào)用了notifyListeners),重新構(gòu)建InheritedProvider ????setState(()?=>?{}); ??} ??@override ??void?didUpdateWidget(ChangeNotifierProvider ?oldWidget)?{ ????//當(dāng)Provider更新時,如果新舊數(shù)據(jù)不"==",則解綁舊數(shù)據(jù)監(jiān)聽,同時添加新數(shù)據(jù)監(jiān)聽 ????if?(widget.data?!=?oldWidget.data)?{ ??????oldWidget.data.removeListener(update); ??????widget.data.addListener(update); ????} ????super.didUpdateWidget(oldWidget); ??} ??@override ??void?initState()?{ ????//?給model添加監(jiān)聽器 ????widget.data.addListener(update); ????super.initState(); ??} ??@override ??void?dispose()?{ ????//?移除model的監(jiān)聽器 ????widget.data.removeListener(update); ????super.dispose(); ??} ??@override ??Widget?build(BuildContext?context)?{ ????return?InheritedProvider ( ??????data:?widget.data, ??????child:?widget.child, ????); ??}}
????上述代碼于ChangeNotifierProvider中提供靜態(tài)方法供子類獲取widget樹中InheritedProvider中保存的共享狀態(tài),_ChangeNotifierProviderState類中主要用來監(jiān)聽共享數(shù)據(jù)改變時重構(gòu)Widget樹,需要注意的是在調(diào)用setstate()方法操作的widget.child始終為同一個,因此執(zhí)行build時,InheritedProvider的child引用的始終是同一個子widget,在對當(dāng)前widget進(jìn)行重新build時,widget.child并不會進(jìn)行重構(gòu),即實現(xiàn)了對widget.child的緩存。需要注意的是如果ChangeNotifierProvider父級Widget重新build時,則其傳入的child便有可能會發(fā)生變化;
????上述即為flutter提供的provider的實現(xiàn)思路,下一天的更新,我們會結(jié)合較為經(jīng)典的購物車功能,來看一下provider的實際使用,今天的分享就到這里,感謝各位看官捧場,讓我們后續(xù)研討學(xué)習(xí)!
