在上一篇文章中我们了解到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广播通知