Stream 学习笔记
如何获取Stream
periodic
void main() {
stream_periodic();
//stream_take();
//stream_takeWhile();
//stream_skip();
//stream_skipWhile();
//stream_toList();
}
int _periodic_callback(int value) {
return 2 * value;
}
stream_periodic() async {
Duration _duration = Duration(seconds: 2);
/**
* 官方注释:
* Creates a stream that repeatedly emits events at [period] intervals.
*
* The event values are computed by invoking [computation]. The argument to
* this callback is an integer that starts with 0 and is incremented for
* every event.
*
* If [computation] is omitted the event values will all be `null`.
*
* 大体意思就是:
*
* _periodic_callback方法计算处理后的值(起始值为0,不断递增),每隔2秒不断的生成包含int结果的事件,
*
* 如果省略了回调,则事件值将全部为null
*
*
*/
Stream stream = Stream.periodic(_duration, _periodic_callback);
//1.
//int v = await stream.first;
//print(v);
//2.
await for (int value in stream) {
print("=>$value");
}
//3. 比方法2友好很多
stream.listen((value) {
print("===>$value");
});
/**
* 1与2或3不能同时解开注释,但2和3 可以同时解开注释,但只会输出监听2;
*
* 因为2和3其实是一样的,单监听一次之后则事件消费
*/
}
输出:
=>0
=>2
=>4
=>6
=>8
=>10
=>12
=>14
=>16
=>18
=>20
=>22
...
无限的输出。。。
注释1与2同时解开之后,会报
Unhandled exception:
Bad state: Stream has already been listened to.
的错误,报错信息也很简单,就是已经被监听过的流不能再次被监听了,其实这也是单订阅流的特点。下面会有一个广播流。
fromFuture
stream_fromFuture() async {
print("start");
Future _future = Future(() {
sleep(Duration(seconds: 2));
return "Sleep 2 秒";
});
//从Future创建一个Stream,可见只能穿一个Future
Stream stream = Stream.fromFuture(_future);
stream.listen((value) {
print("=>${value}");
});
print("end");
}
输出:
start
end
=>Sleep 2 秒
当Future任务执行完成后,将结果放入Stream中,而后从Stream中将任务完成的结果取出。这种用法,很像异步任务队列。
fromFutures
stream_fromFutures() async {
Future _future1 = Future(() {
return "我没睡";
});
Future _future2 = Future(() {
sleep(Duration(seconds: 2));
return "Sleep 2 秒";
});
Stream stream = Stream.fromFutures([_future2, _future1]);
stream.listen((value) {
print("=>${value}");
});
}
输出:
=>我没睡
=>Sleep 2 秒
fromIterable
stream_fromIterable() async{
Stream stream =Stream.fromIterable(["路人甲","炮灰乙","流氓丙"]);
stream.listen((value){
print("=>${value}");
});
}
输出:
=>路人甲
=>炮灰乙
=>流氓丙
Stream 里面的一些方法
listen
监听流,前面一直在用的。
take
截取一定数量的Stream。
stream_take() async {
Duration _duration = Duration(seconds: 2);
Stream stream = Stream.periodic(_duration, _periodic_callback);
//只截取了前5个事件,之后Stream会关闭
stream = stream.take(5);
stream.listen((value) {
print("===>$value");
});
}
输出:
===>0
===>2
===>4
===>6
===>8
takeWhile
stream_takeWhile() async {
Duration _duration = Duration(seconds: 2);
Stream stream = Stream.periodic(_duration, _periodic_callback);
//筛选符合条件的事件
stream = stream.takeWhile(condition);
stream.listen((value) {
print("===>$value");
});
}
bool condition(int value) {
//不满足条件的不监听
return value < 12;
}
输出:
===>0
===>2
===>4
===>6
===>8
===>10
skip
stream_skip() async {
Duration _duration = Duration(seconds: 2);
Stream stream = Stream.periodic(_duration, _periodic_callback);
stream = stream.take(10);
//跳过前两个事件
stream = stream.skip(2);
stream.listen((value) {
print("===>$value");
});
}
输出:
===>4
===>6
===>8
===>10
===>12
===>14
===>16
===>18
stream_skipWhile
stream_skipWhile() async {
Duration _duration = Duration(seconds: 2);
Stream stream = Stream.periodic(_duration, _periodic_callback);
stream = stream.take(10);
//跳转到满足值得事件
stream = stream.skipWhile(condition);
stream.listen((value) {
print("===>$value");
});
}
bool condition(int value) {
//不满足条件的不监听
return value < 12;
}
skipWhile 和takeWhile的用法是一样,都是筛选满足条件的Stream。
toList
stream_toList() async {
Duration _duration = new Duration(seconds: 2);
Stream stream = Stream.periodic(_duration, _periodic_callback);
stream = stream.take(5);
//因为是异步的,所以有 await,返回已经完成的事件列表
List datas = await stream.toList();
for (int i in datas) {
print("===>$i");
}
}
Future> toList() 表示将Stream中所有数据存储在List中。
length
取得Stream中所有数据的长度。
我们发现这个Stream这个流控制起来不太方便,不能想在指定的时刻投送事件或者关闭流,这个时候StreamController就起到了重要的辅助作用。
StreamController
import 'dart:async';
void main() {
//初始化"单订阅"流控制器
StreamController ctrl = new StreamController();
//初始化一个subscription 订阅者
StreamSubscription subscription = ctrl.stream
.listen(onListen,
onError: onError,
onDone: onDone,
cancelOnError: false);//cancelOnError 当有Error的时候是否关闭流
//流流入数据,能够控制何时投递消息
ctrl.sink.add('Hello World');
ctrl.sink.add(1234);
ctrl.sink.addError("onError!");
ctrl.sink.add(13.14);
ctrl.close();
}
void onListen(event) {
print("==>${event}");
}
void onError(error) {
print(error);
}
void onDone() {
print('The stream is done !');
}
输出:
==>Hello World
==>1234
onError!
==>13.14
The stream is done !
- onData
监听流事件。
- onError
监听error的事件流。
- onDone
当一个 Stream 调用close()方法后,发送了 done 事件,这个方法会被调用。
- cancelOnError
当 Stream 碰到 Error 事件的时候,是否关闭这个 Stream。
广播流
其实也好理解,单订阅流就是流里面的东西是第一个订阅者私有的,不允许他人染指,而广播流,则表示流里面的东西是公有的。
import 'dart:async';
//广播流
void main() {
StreamController ctrl = new StreamController.broadcast();
// 第一个订阅者
StreamSubscription subscription1 =
ctrl.stream.listen((value) => print('第一个订阅者 $value'));
// 第二个订阅者
StreamSubscription subscription2 =
ctrl.stream.listen((value) => print('第二个订阅者 $value'));
ctrl.sink.add(2);
ctrl.sink.add(4);
ctrl.close();
}
输出:
第一个订阅者 2
第二个订阅者 2
第一个订阅者 4
第二个订阅者 4
StreamTransformer
流的转换,流现在可以看做是一个传送数据的管道,或者传送带,在管道的一端投送事件,在另一端接受事件中的数据,但是在这一管道传送过程中数据是可以转换的。传入的是int类型,输出的是double类型,这就是转换。
map 转换
import 'dart:async';
void main() {
StreamController ctl = StreamController();
ctl.stream
.map((value) => Data((value * 2).toString())) // int 转换为 Data 类型
.listen((value) => print(value));
ctl.sink.add(3);
ctl.close();
}
class Data {
final String name;
Data(this.name);
@override
String toString() {
// TODO: implement toString
return "data=> ${this.name}";
}
}
输出:
data=> 6
StreamTransformer.fromHandlers
import 'dart:async';
//由int类型转换为Data类型
void main() {
StreamController ctl = StreamController();
// 创建 StreamTransformer对象
StreamTransformer stf = StreamTransformer.fromHandlers(
handleData: (int data, EventSink sink) {
// 操作数据后,转换为 Data 类型
sink.add(Data((data * 2).toString()));
},
);
// 调用流的transform方法,传入转换对象
Stream stream = ctl.stream.transform(stf);
stream.listen(print);
// 添加数据,这里的类型是int
ctl.add(3);
// 调用后,触发handleDone回调
ctl.close();
}
class Data {
final String name;
Data(this.name);
@override
String toString() {
// TODO: implement toString
return "data=> ${this.name}";
}
}
结果都是一样的。
Stream 在Flutter中的运用
Stream可以刷新 StatelessWidget控件里面的UI。
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HomePage(),
title: 'Stream Demo',
));
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Stream Demo'),
),
body: Center(
child: StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(
'1 Minute Completed',
style: TextStyle(
fontSize: 30.0,
),
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Text(
'Waiting For Stream',
style: TextStyle(
fontSize: 30.0,
),
);
}
return Text(
'00:${snapshot.data.toString().padLeft(2,'0')}',
style: TextStyle(
fontSize: 30.0,
),
);
},
initialData: 0,
stream: _stream(),
),
),
);
}
Stream _stream() {
Duration interval = Duration(seconds: 1);
Stream stream = Stream.periodic(interval, transform);
stream = stream.take(59);
return stream;
}
int transform(int value) {
return value;
}
}
效果图
HomePage继承一个无状态的组件,是无法用setState刷新界面的,但是Stream可以帮我们在无状态组件中刷新UI。
总结
Stream可以看作是一条管道,管道的中间有一个加工房,通过sink在管道中投放事件,加工房通过map,StreamTransformer可以对管道中的数据进行加工转换,最后由listen监听处理结果。其中涉及到的类:
- Stream
流,事件(数据)的包裹类。
- StreamController
Stream的控制管理类
- StreamSink
事件的输入口(sink.add)
- StreamSubscription
订阅者
最后
最后厚颜贴一张自己学习Flutter的公众号,感兴趣的小伙伴可以一起学习哦。。。