参考文章:Reactive Programming - Streams - BLoC
(为了便于阅读,略去了原文中的一些跟StreamBuilder和Bloc无关的拓展概念,比如RxDart、Demo的解释等,想要进一步了解的可以移步原文。)
先粗略讲点关于Stream的东西
Stream其实类似于Rx大家族,也是一种对于数据流的订阅管理。Stream可以接受任何类型的数据,值、事件、对象、集合、映射、错误、甚至是另一个Stream,通过StreamController
中的sink
作为入口,往Stream中插入数据,然后通过你的自定义监听StreamSubscription
对象,接受数据变化的通知。如果你需要对输出数据进行处理,可以使用StreamTransformer
,它可以对输出数据进行过滤、重组、修改、将数据注入其他流等等任何类型的数据操作。
Stream有两种类型:单订阅Stream和广播Stream。单订阅Stream只允许在该Stream的整个生命周期内使用单个监听器,即使第一个subscription被取消了,你也没法在这个流上监听到第二次事件;而广播Stream允许任意个数的subscription,你可以随时随地给它添加subscription,只要新的监听开始工作流,它就能收到新的事件。
下面是一个单订阅的例子:
import 'dart:async';
void main() {
// 初始化一个单订阅的Stream controller
final StreamController ctrl = StreamController();
// 初始化一个监听
final StreamSubscription subscription = ctrl.stream.listen((data) => print('$data'));
// 往Stream中添加数据
ctrl.sink.add('my name');
ctrl.sink.add(1234);
ctrl.sink.add({'a': 'element A', 'b': 'element B'});
ctrl.sink.add(123.45);
// StreamController用完后需要释放
ctrl.close();
}
下面是添加了StreamTransformer的例子:
import 'dart:async';
void main() {
// 初始化一个int类型的广播Stream controller
final StreamController<int> ctrl = StreamController<int>.broadcast();
// 初始化一个监听,同时通过transform对数据进行简单处理
final StreamSubscription subscription = ctrl.stream
.where((value) => (value % 2 == 0))
.listen((value) => print('$value'));
// 往Stream中添加数据
for(int i=1; i<11; i++){
ctrl.sink.add(i);
}
// StreamController用完后需要释放
ctrl.close();
}
关于RxDart
之前已经说了,Stream是一种订阅者模式,所以跟Rx大家族很类似。Rx官方已经提供了对Dart语言的官方支持——RxDart。两者的对应关系可以看下下表:
Dart | RxDart |
---|---|
Stream | Observable |
StreamController | Subject |
对于RxDart的用法这里不多做讨论。
Flutter中Widget的状态管理和响应式编程的概念
我们都知道,Flutter中Widget的状态控制了UI的更新,比如最常见的StatefulWidget,通过调用setState({})
方法来刷新控件。那么其他类型的控件,比如StatelessWidget就不能更新状态来吗?答案当然是肯定可以的。比如Flutter的Redux
插件,就是一种在非StatefulWidget中刷新控件的机制。
我们上面已经说了,Stream的特性就是当数据源发生变化的时候,会通知订阅者,那么我们是不是可以延展一下,实现当数据源发生变化时,改变控件状态,通知控件刷新的效果呢?Flutter为我们提供了StreamBuilder
。
所以,StreamBuilder是Stream在UI方面的一种使用场景,通过它我们可以在非StatefulWidget中保存状态,同时在状态改变时及时地刷新UI。
(Flutter还有其他的一些管理状态的方法跟插件,鄙人暂时没有研究过其他,就不多说了。)
其实这种数据源改变,UI也跟着改变的方式就是一种响应式编程(Reactive Programming)。响应式编程就是使用异步数据流来编程的方式,换句话说,任何事件(比如点击事件)、变量、消息、请求等等的改变,都会触发数据流的传递。
如果使用响应式编程,那么App将:
- 变为异步的; <