开发环境:
Mac OS 10.14.5
VSCode 1.36.1
Flutter 1.9.1+hotfix.2
前面已经有 Flutter示例系列(二)之状态管理-壹(scoped_model)、 Flutter示例系列(二)之状态管理-贰(fish-redux)和 Flutter示例系列(二)之状态管理-叁(Bloc)。
基于ReactiveX,很多语言目前都有Rx的版本。由于Rx同出一脉,所以之前用过其他语言的Rx版本,可能比较好上手。当然很遗憾,我还没用过(哈哈哈哈哈哈哈哈哈)下面就一起来瞅一瞅。
RxDart 是基于 ReactiveX 的响应式函数编程库。Dart本身有 Stream API,RxDart在其上增加了其他方法。在 Dart基础语法之异步支持 中(见公众号:Flutter小同学),简单描述了Stream的用法,在 Dart异步编程:Streams(见公众号:Flutter小同学) 中,对Stream进行了介绍。
类
1. Observable
RxDart的 Observable 继承自 Stream。
示例:
new Observable(new Stream.fromIterable([1]))
.interval(new Duration(seconds: 1))
.flatMap((i) => new Observable.just(2))
.take(1)
.listen(print); // prints 2
Observable 类和运算符 是对Stream和StreamTransformer类的简单包装。所有的基础实现都可以与Observable类一起使用。
Observable与Stream的区别?
1.Cold Observable是单订阅。除非是hot(又称广播) Stream,否则仅可监听Observable一次。如果尝试监听两次会有异常抛出。如果需要监听多次,可以创建一个返回stream新实例的工厂方法。
2.大多数方法如 first 和 last 不会返回一个 Single或者Observable,但必定返回Future。可以使用 myFuture.asStream() 轻松转换成 Stream。
3.当有错误发生,Stream默认不关闭。在Rx中,错误会导致Observable终止,除非被其他操作打断。其实Dart有这种机制,当错误发生时将创建的streams关闭,但是大多数没有显示这一行为。
4.Streams默认异步,但是Observable默认同步,除非在不同的计划程序上制定工作。也可创建同步的Streams。
5.当使用广播Streams(类似Hot Observable)时,注意 onListen 仅在第一次监听到广播Stream时被调用。
2. PublishSubject
就像普通的广播 StreamController一样,但是返回的是一个 Observable 而不是 Stream。
允许向监听者发送数据、错误和完成的事件。
默认情况下,PublishSubject 是一个广播(又称hot)控制器,可多次订阅。
示例:
final subject = new PublishSubject<int>();
// observer1 will receive all data and done events
subject.stream.listen(observer1);
subject.add(1);
subject.add(2);
// observer2 will only receive 3 and done event
subject.stream.listen(observe2);
subject.add(3);
subject.close();
继承链:
Object > Observable > Subject > PublishSubject
3. BehaviorSubject
作为一个特殊的StreamController,捕获添加进控制器的最新项,并且作为第一项发送给新的监听者。
允许向监听者发送数据、错误和完成的事件。把添加进来的最新项发送给新的监听者,之后,新事件将适当的发送给监听者。如果没有新项加入,可能提供原始值。
默认情况下,BehaviorSubject 是一个广播(又称hot)控制器,可多次订阅。
示例:
final subject = new BehaviorSubject<int>();
subject.add(1);
subject.add(2);
subject.add(3);
subject.stream.listen(print); // prints 3
subject.stream.listen(print); // prints 3
subject.stream.listen(print); // prints 3
返回原始值的示例:
final subject = new BehaviorSubject<int>.seeded(1);
subject.stream.listen(print); // prints 1
subject.stream.listen(print); // prints 1
subject.stream.listen(print); // prints 1
继承链:
Object > Observable > Subject > BehaviorSubject
4. ReplaySubject
作为一个特殊的StreamController,捕获所有已被添加的项,作为第一项发送给新的监听者。
允许向监听者发送数据、错误和完成的事件。会存储被添加进来的项,并发送给监听者,之后,新事件将适当的发送给监听者。可以限制存储的最大数量。
默认情况下,ReplaySubject 是一个广播(又称hot)控制器,可多次订阅。
示例:
final subject = new ReplaySubject<int>();
subject.add(1);
subject.add(2);
subject.add(3);
subject.stream.listen(print); // prints 1, 2, 3
subject.stream.listen(print); // prints 1, 2, 3
subject.stream.listen(print); // prints 1, 2, 3
限制大小的示例:
final subject = new ReplaySubject<int>(maxSize: 2);
subject.add(1);
subject.add(2);
subject.add(3);
subject.stream.listen(print); // prints 2, 3
subject.stream.listen(print); // prints 2, 3
subject.stream.listen(print); // prints 2, 3
继承链:
Object > Observable > Subject > ReplaySubject
创建项目
详情见 Flutter示例系列(一)之创建项目
创建 git_search_rxdart 项目,用于搜索github。注释详情参考Demo。
在 pubspec.yaml 文件中引入 rxdart 和 http 依赖库:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
rxdart: ^0.22.2
http: ^0.11.3+17
github_api.dart
创建 GithubAPI类,用于请求数据,并对已获取的内容进行缓存。
还创建SearchResultItem(项目model) 和 SearchResult(mdoel集合) 类。
组件
empty_result_widget.dart:搜索结果为空的组件
search_error_widget.dart:搜索失败的组件
search_intro_widget.dart:输入关键词的组件
search_loading_widget.dart:正在加载的组件
search_result_widget.dart:展示搜索结果的组件
github_widget.dart:整个页面(后续完成)的组件
GitHub_state.dart
创建SearchState类及其子类,定义搜索状态。
包含子类:
SearchLoading:正在加载的状态
SearchError:搜索失败的状态
SearchNoTerm:搜索无结果的状态
SearchPopulated:搜索有结果的状态
SearchEmpty:搜索结果为空的状态
github_bloc.dart
创建 SearchBloc 类,进行状态管理。
定义实例变量 onTextChanged,将文本内容放入Sink
final Sink<String> onTextChanged;
定义实例变量 state,存储状态变化
final Stream<SearchState> state;
定义工厂方法,供外界调用并初始化SearchBloc。
factory SearchBloc(GithubAPI api)
其他部分实现查看代码。
github_widget.dart
创建 SearchScreen类,并在此初始化 SearchBloc类。
bloc = SearchBloc(widget.api);
使用 StreamBuilder 来展示异步数据变化
StreamBuilder<SearchState>(
stream: bloc.state,
initialData: SearchNoTerm(),
builder: (BuildContext context, AsyncSnapshot<SearchState> snapshot) {
其他部分实现查看代码。
至此,对RxDart做了简单的介绍,并完成一个小示例。Flutter状态管理系列也告一段落。选择合适的状态管理方案,关乎整个项目的基石方向。阿里的闲鱼社区应该是国内目前最活跃的,也是切身实践使用Flutter构建应用的。
如果选择,我可能会使用 fish-redux,但是bloc和rxdart已经被广泛使用,所以选择只是方向,运用才是目的。
参考文档:
RxDart
Flutter | 状态管理拓展篇——RxDart(四)
RxDart: Magical transformations of Streams
这可能是最早的RxDart使用入门教程
=================================================================
个人博客
Github
个人公众号:Flutter小同学
个人网站