Stream在Flutter中也是属于非常关键的概念,就好比前面我有写过一篇event_bus的文章,我就是用Stream去做的,有兴趣的可以去前面看看。
在Flutter中,状态管理出了本身自带的InheritedWidget
之外,还有什么rxdart,Bolc,redux,provider以及别的大佬们写的插件。但是不管是什么状态管理的模式,我看到的基本都是离不开Stream的封装。
通俗的来讲,这个Stream就是事件流或者管道,事件流大家都不会太陌生吧,简单的来说就是:基于事件流驱动设计代码,类似于event_bus,先注册ranho-u再监听订约时间,并且针对事件变换处理不同的响应。
我有幸看过一篇文章,但是忘了是哪位大佬的了,在Flutter中,既然我们谈到了Stream,那么我们自然就不能丢掉Stream的“四大天王”:StreamController,Sink,Stream,StreamSubscription。
好了不多说了,我们就开始进入正题,慢慢的去了解Stream是怎么去进行操作的。
最简单的,我们都知道,我们在使用任何东西的同时,一定要先定义吧,然后创建注册和监听。
class StreamDemoPage extends StatefulWidget {
@override
_StreamDemoPageState createState() => _StreamDemoPageState();
}
class _StreamDemoPageState extends State {
void onData(String data){ //data参数表示的就是_streamDemo这个Stream出来的数据
print('$data');
}
Future fetchData() async{
await Future.delayed(Duration(seconds: 2)); //延迟2s获取数据
return "我是Stream中的数据";
}
@override
void initState() {
super.initState();
//创建Stream
print('创建一个Stream');
Stream _stream = Stream.fromFuture(fetchData()); //创建一个Stream并且从一个Future中获取数据
print('开始监听这个Stream');
_stream.listen(onData);
print('完成');
}
@override
Widget build(BuildContext context) {
return Container();
}
}
这是我们的代码,然后看控制台打印:
这个代码应该很简单只要你看就能看懂我就不多说了。
进行进一步的修改,我们给Stream添加一个订阅Subscription
class _StreamDemoPageState extends State {
void onData(String data){ //data参数表示的就是_streamDemo这个Stream出来的数据
print('$data');
}
void onDone(){
print('-----Done-----!');
}
void onError(error){
print('Error: $error');
}
Future fetchData() async{
await Future.delayed(Duration(seconds: 2));//延迟3秒
return "我是Stream中的数据";
}
@override
void initState() {
super.initState();
//创建Stream
print('创建一个Stream');
Stream _stream = Stream.fromFuture(fetchData());
print('开始监听这个Stream');
_stream.listen(onData,onError: onError,onDone: onDone); // -- 1 --
print('完成');
}
@override
Widget build(BuildContext context) {
return Container();
}
}
然后还是老规矩,代码完成后看控制台打印:
好了这里你会说,不是给Stream加了一个Subscription订阅吗?在哪里加的?看:
可以看出注释 - - 1 - - 处,listen的返回值就是我们要添加的订阅,只是加了处理,监听之后我们需要做的时间,onError那就是错误的处理,onDone就是我们在监听之后需要做的。
然后我们在看一下关于订阅就是我们listen()
方法的一些进一步的操作。 ----暂停 恢复 取消
class _StreamDemoPageState extends State {
StreamSubscription _streamSubscription;
void onDone() {
print('-----Done-----!');
}
void onError(error) {
print('Error: $error');
}
void onData(String data) {
//data参数表示的就是_streamDemo这个Stream出来的数据
print('$data');
}
Future fetchData() async {
await Future.delayed(Duration(seconds: 2));
return "我是Stream中的数据";
}
void _pauseStream() {
print('暂停');
_streamSubscription.pause();
}
void _resumeStream() {
print('恢复');
_streamSubscription.resume(); //恢复以后得到Stream上面的数据就会把它输出到控制台上
}
void _cancelStream() {
print('取消');
_streamSubscription.cancel(); //取消以后就不能使用stream上面的数据了,也没有办法恢复
}
@override
void initState() {
super.initState();
//创建Stream
print('创建一个Stream');
Stream _streamDemo = Stream.fromFuture(fetchData());
print('开始监听这个Stream');
_streamSubscription =
_streamDemo.listen(onData, onError: onError, onDone: onDone);
print('完成');
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Text('Pause'),
color: Theme.of(context).accentColor,
onPressed: _pauseStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Resume'),
color: Theme.of(context).accentColor,
onPressed: _resumeStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Cancel'),
color: Theme.of(context).accentColor,
onPressed: _cancelStream,
),
],
),
),
);
}
}
控制台打印我就不放了,初始没变。
我这里也只说一下功能就好了,相信用过event_bus的时候都知道,我们都会有暂停和**取消(销毁)**一说吧,我们如果暂停之后,这个订阅就不会在输出任何数据,其实这个订阅是收到了数据了,但是我们给他暂停了,只要当我们在恢复的时候输出数据。这样说有点乱,不急看完下面就会明白的。
为了进一步的去了解这个过程,我们使用StreamController进行管理和控制Stream。
场景: 我们先按一下add按钮,然后会输出数据,在按下pause按钮,会发现我们无论怎么去按add都没有输出数据,最后我们按下resume按钮才会有数据。
class _StreamDemoPageState extends State {
StreamSubscription _streamSubscription;
StreamController _streamController;
void onDone() {
print('-----Done-----!');
}
void onError(error) {
print('Error: $error');
}
void onData(String data) {
//data参数表示的就是_streamDemo这个Stream出来的数据
print('$data');
}
Future fetchData() async {
await Future.delayed(Duration(seconds: 2));
return "我是Stream中的数据";
}
void _addDataToStream() async {
print('添加');
String data = await fetchData();
_streamController.add(data);
}
void _pauseStream() {
print('暂停');
_streamSubscription.pause();
}
void _resumeStream() {
print('恢复');
_streamSubscription.resume(); //恢复以后得到Stream上面的数据就会把它输出到控制台上
}
void _cancelStream() {
print('取消');
_streamSubscription.cancel(); //取消以后就不能使用stream上面的数据了,也没有办法恢复
}
@override
void initState() {
super.initState();
//创建Stream
print('创建一个Stream');
// Stream _streamDemo = Stream.fromFuture(fetchData());
_streamController = new StreamController(); //这里去实例化
print('开始监听这个Stream');
_streamSubscription = _streamController.stream.listen(onData,onError: onError,onDone: onDone);
print('完成');
}
@override
void dispose() {
super.dispose();
_streamSubscription.cancel();
_streamController.close();
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Text('Add'),
color: Theme.of(context).accentColor,
onPressed: _addDataToStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Pause'),
color: Theme.of(context).accentColor,
onPressed: _pauseStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Resume'),
color: Theme.of(context).accentColor,
onPressed: _resumeStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Cancel'),
color: Theme.of(context).accentColor,
onPressed: _cancelStream,
),
],
),
),
);
}
}
这样就比较适合我们场景的设计模式了。有兴趣的可以直接复制下来自己去操作一下就明白了。
这里我就强调一点,我们使用StreamController去控制Stream的时候,一定要控制好进程,如果不对可能你的项目问题就会很大的。、
我们可以使用Sink往Stream上添加数据
class _StreamDemoPageState extends State {
StreamSubscription _streamSubscription;
StreamController _streamController;
StreamSink _streamSink;
void onDone() {
print('-----Done-----!');
}
void onError(error) {
print('Error: $error');
}
void onData(String data) {
//data参数表示的就是_streamDemo这个Stream出来的数据
print('$data');
}
Future fetchData() async {
await Future.delayed(Duration(seconds: 2));
return "我是Stream中的数据";
}
void _addDataToStream() async {
print('添加');
String data = await fetchData();
_streamController.add(data);
}
void _pauseStream() {
print('暂停');
_streamSubscription.pause();
}
void _resumeStream() {
print('恢复');
_streamSubscription.resume(); //恢复以后得到Stream上面的数据就会把它输出到控制台上
}
void _cancelStream() {
print('取消');
_streamSubscription.cancel(); //取消以后就不能使用stream上面的数据了,也没有办法恢复
}
@override
void initState() {
super.initState();
//创建Stream
print('创建一个Stream');
// Stream _streamDemo = Stream.fromFuture(fetchData());
_streamController = new StreamController(); //这里去实例化
_streamSink = _streamController.sink; //_streamController.sink 他这里会给我们返回一个StreamSink
print('开始监听这个Stream');
_streamSubscription = _streamController.stream.listen(onData,onError: onError,onDone: onDone);
print('完成');
}
@override
void dispose() {
super.dispose();
_streamSubscription.cancel();
_streamController.close();
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Text('Add'),
color: Theme.of(context).accentColor,
onPressed: _addDataToStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Pause'),
color: Theme.of(context).accentColor,
onPressed: _pauseStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Resume'),
color: Theme.of(context).accentColor,
onPressed: _resumeStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Cancel'),
color: Theme.of(context).accentColor,
onPressed: _cancelStream,
),
],
),
),
);
}
}
我们仅需要点击Add按钮
然后控制台会有响应的输出如下:
其余的pause resume cancel 我就不说了,其实和我们上面说的逻辑是一样的,而且得到的结果也是一样,有兴趣的可以自己去跑一遍试试。
在这里解释一下: sink是什么?sink就想是**“槽”,有人说“槽”时什么?如果你接触过Qt** 那么相信你对于槽信号这边应该不难理解。如果你实现是很难理解就当做成一个订阅者和发布者来理解就好了!
感觉他应该也说完了,在上面都有这个Subscription插入,有兴趣的还是想了解了可以去看一下源码,如果实在不想看源码就可以看看我前面写的event_bus广播的文章。
StringBuilder
是根据Stream上的数据去构建WIdget
,Stream上的数据变化时,他会自己去重构Widget
不需要我们去setState()
class _StreamDemoPageState extends State {
StreamSubscription _streamSubscription;
StreamController _streamController;
StreamSink _streamSink;
String _data;
void onDone() {
print('-----Done-----!');
}
void onError(error) {
print('Error: $error');
}
void onData(String data) {
//data参数表示的就是_streamDemo这个Stream出来的数据
print('$data');
}
Future fetchData() async {
await Future.delayed(Duration(seconds: 2));
return "我是Stream中的数据";
}
void _addDataToStream() async {
print('添加');
String data = await fetchData();
_streamController.add(data);
}
void _pauseStream() {
print('暂停');
_streamSubscription.pause();
}
void _resumeStream() {
print('恢复');
_streamSubscription.resume(); //恢复以后得到Stream上面的数据就会把它输出到控制台上
}
void _cancelStream() {
print('取消');
_streamSubscription.cancel(); //取消以后就不能使用stream上面的数据了,也没有办法恢复
}
@override
void initState() {
super.initState();
//创建Stream
print('创建一个Stream');
// Stream _streamDemo = Stream.fromFuture(fetchData());
_streamController = new StreamController(); //这里去实例化
_streamSink = _streamController.sink; //_streamController.sink 他这里会给我们返回一个StreamSink
print('开始监听这个Stream');
// _streamSubscription = _streamController.stream.listen(onData,onError: onError,onDone: onDone);
print('完成');
}
@override
void dispose() {
super.dispose();
_streamSubscription.cancel();
_streamController.close();
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
children: [
StreamBuilder(
stream: _streamController.stream,
initialData: _data,
builder: (context,snapshot){
return Text('${snapshot.data}');
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Text('Add'),
color: Theme.of(context).accentColor,
onPressed: _addDataToStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Pause'),
color: Theme.of(context).accentColor,
onPressed: _pauseStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Resume'),
color: Theme.of(context).accentColor,
onPressed: _resumeStream,
),
SizedBox(width: 10,),
FlatButton(
child: Text('Cancel'),
color: Theme.of(context).accentColor,
onPressed: _cancelStream,
),
],
),
],
),
),
);
}
}
然后我们点击一下Add按钮
这样就有值了!!!在这里的补充我们顺便说了StreamBuilder
的使用!
我们然后看一下控制台的打印:
就这样完成了!!!!其余三个按钮的功能相信我不说你也能猜到了,如果自己很想知道可以自己去试一下代码复制下来就好。