在上一篇文章中我們了解到Stream及響應式編程的相關概念,本文著重介紹在引入 RxDart 三方庫后,不同Subject在實際開發中的應用場景。
成都創新互聯專業為企業提供點軍網站建設、點軍做網站、點軍網站設計、點軍網站制作等企業網站建設、網頁設計與制作、點軍企業網站模板建站服務,10多年點軍做網站經驗,不只是建網站,更提供有價值的思路和整體網絡服務。
先附上演示項目代碼: flutter_movie
RxDart 是基于 ReactiveX 標準API的Dart版本實現,由Dart標準庫中Stream擴展而成。因此,RxDart與Dart的相關術語稍有區別:
Observable 等同于 Stream , Subject 等同于 StreamController ,前者均由后者繼承而來。
不同于Dart,RxDart提供了三種StreamController的變體來應用到不同的場景:
PublishSubject是最普通的廣播StreamController,和StreamController唯一的區別是它返回對象是Observable,而StreamController返回的是Stream。
PublishSubject PublishSubject
從圖中可以了解到,listener只能監聽到訂閱之后的事件。
示例代碼:
BehaviorSubject也是廣播StreamController,和PublishSubject的區別是它會額外返回訂閱前的最后一次事件。
BehaviorSubject BehaviorSubject
示例代碼:
ReplaySubject也是廣播StreamController,從字面上可以了解到它可以回放已經消失的事件。
ReplaySubject ReplaySubject
示例代碼:
上文提到 StreamBuilder 作為Flutter中根據Stream生成Widget的便利工具,這里結合Subject來一起使用。
tab_bar.dart - 01 tab_bar.dart - 01 tab_bar.dart - 02 tab_bar.dart - 02
以上代碼實現了一個簡單的TabBar(或Segment),該Widget被定義成為一個StatefulWidget(因為它在點擊交互后有更新高亮索引的需求)。
在 build 函數內部我們就使用了StreamBuilder來構建UI,它監聽的數據源為BLoc的 tabList ,在builder閉包內部才是實際的UI構建代碼。
tab_bloc.dart tab_bloc.dart
至于 tab_bloc.dart 則更加簡單,他從構造函數接收一個字符串類型的List,然后使用ReplaySubject包裝數據,
同時暴露出stream類型的對象 tabList 供外部訂閱(即 tab_bar.dart 中第44行代碼中的stream)。
最后,我們來看看完整的調用流程。
main.dart main.dart
在 main.dart 第30~50行代碼中,我們使用了 BlocProvider 來管理bloc(對于BlocProvider深入了解可以參考上一篇文章的相關部分),
provider包裹的是整個 HomePage 這使得在page內部任何地方訪問到bloc成為可能。
home.dart home.dart
在 home.dart 第38,39行代碼中,我們使用provider獲取bloc,并完成對TabBar的初始化。
更進一步,上篇文章提到的StreamTransformer可以對Stream進行相應的處理,同樣的,RxDart中也支持類似的操作,它們被稱為 操作符 。
search_list_bloc.dart search_list_bloc.dart
在上述稍顯復雜的示例中,實現了基本的分頁搜索功能。其中13~17行代碼完成搜索索引位置監聽前的處理,包括 采樣 (按時間采樣的緩存操作)、過濾、去重操作。
換句話說,在最終監聽到start之前,我們都可以對Observable對象進行一系列地處理以達到數據使用方的需求。
相信很多人對于Flutter系列的開篇以響應式編程為主題表示有點異議,但谷歌官方推行這一架構必然有其合理性,無論是狀態管理上還是Dart的原生支持上而言。
在學習新技術時不僅要學習語言的語法,更要理解其架構思想(新瓶裝舊酒,Flutter和Dart是新瓶,響應式編程的思想是舊酒),將思維轉變過來才能更加快速地獲得新技術為我們帶來的便利性。
后續的文章將圍繞文章開頭的demo項目進行展開,盡請期待~
Flutter狀態管理系列:
Flutter狀態管理(一):ScopedModel
Flutter狀態管理(二):Provider
Flutter狀態管理(三):BLoC(Business Logic Component)
Flutter狀態管理(四):ReactiveX之RxDart
Flutter狀態管理(五):Redux
有做過H5前端開發的朋友應該很早就接觸過這個,Redux在React/VUE中,與在Flutter/Dart中概念一樣,沒有任何區別;唯一的區別只是使用上的不同。
它主要由三部分組成:
下圖是一個完整的數據觸發及更新流程:
我們看到上面整個數據流,都是單向的,由View發起,最后到View的更新;
為啥這樣設計?
小節二介紹了Redux最基本的原理,但是,如何用Redux來做一些異步操作,比如:加載數據、請求API等?這里就引出來了Redux的中間件(Middleware),中間件能夠讓我們使得action在到達reducer之前,做些其它“動作”!有了中間件,我們不但可以請求API,還可以改變action,使得分發到其它reducer中去;
上圖是有Middleware的流程圖。
Redux在Flutter中的使用與在JavaScript中的使用方式稍微有點不同,為啥?
因為JavaScript是弱類型語言,而Dart是強類型語言,這就使得在JS中每個reducer可以獨立管理,而在Flutter中需要由一個大對象來管理!
無論在JS中還是在Flutter中,通常都將action、reducer、store各自建一目錄,放在redux目錄下,目錄結構如下:
ReduxPage在build中,也可以直接用StoreBuilder(參考ReduxPage2中寫法),因為StoreBuilder也是InheritedWidget。
正因為Redux在Flutter中與在JS中不同,因此,在Flutter中,建議:
初始化問題:這邊初始化是在bloc里,直接在構造方法里面賦初值的,state中一旦變量多了,還是這么寫,會感覺極其難受,不好管理。需要優化
如果進行一個頁面,需要進行復雜的運算或者請求接口后,才能知曉數據,進行賦值,這里肯定需要一個初始化入口,初始化入口需要怎樣去定義呢?
首先對state進行優化,新增倆個方法:init()和clone()
init():這里初始化統一用init()方法去管理。
clone():這邊克隆方法,是非常重要的,一旦變量達到倆位數以上,就能深刻體會該方法是多么的重要。
定義一個與初始化state相對應的init()初始化方法
這增加了初始化方法,請注意,如果需要進行異步請求,同時需要將相關邏輯提煉一個方法,咱們在這里配套Future和await就能解決在異步場景下同步數據問題.
這里使用了克隆方法,可以發現,我們只要關注自己需要改變的變量就行了,其它的變量都在內部賦值好了,我們不需要去關注;這就大大的便捷了頁面中有很多變量,只需要變動一倆個變量的場景。
view層增加了個初始化事件。初始化操作直接在創建的時候,在XxxBloc上使用add()方法就行了,就能起到進入頁面,初始化一次的效果。
bloc的思想
觀察者模式的思想;觀察者(回調刷新控件)和被觀察者(產生相應事件,添加事件,去通知觀察者),bloc層是處于觀察者和被觀察者中間的一層,我們可以在bloc里面搞業務,搞邏輯,搞網絡請求;拿到Event事件傳遞過來的數據,把處理好的、符合要求的數據返回給view層的觀察者就行了。
1、繼承SingleChildStatelessWidget,就是一個widget,通過create 傳入一個Bloc對象
1、Bloc繼承自BlocBase,BlocBase中創建了StreamController對象,為多訂閱對象
其中onCounterEvent((event, emit)為初始化創建_eventController監聽
2、Bloc中創建_eventController,為事件通知
3、BlocBase創建_stateController,為狀態刷新通知
4、add方法是執行廣播通知
5、處理完數據之后執行emit()方法,其中emit方法是stateController廣播
1、 BlocBuilder繼承自BlocBuilderBase,_BlocBuilderBaseState中build方法返回的是BlocListener
2、BlocListener繼承BlocListenerBase,_BlocListenerBaseState中_subscribe()添加監聽stateController廣播通知