Flutter开发·Stream的理解与简单使用

823 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

介绍

Stream和Future都是在Flutter中常用来处理异步事件的对象,与Future只能处理单次异步操作不同的是,Stream具有多次响应异步事件监听的功能,是一系列异步事件的序列。

分类

Stream从订阅模式上分可以分为两类,一个是单订阅模式,另一个是多订阅模式,也称广播模式,单订阅模式表示只能有1个监听器对事件进行监听,即使前面的监听器取消了监听,也无法继续对这个stream进行二次监听,而多订阅模式则可以有多个监听器。

组成

Stream中主要包含了四个对象:

  • Stream:事件源,一般用于事件监听或事件转换等。
  • StreamController: 方便进行Stream管理的控制器。
  • StreamSink: 事件的输入口,包含add等方法进行事件发送。
  • StreamSubscription: Stream进行listen监听后得到的对象,用来管理事件订阅,包含取消监听等方法。

写法

单订阅模式

直接使用StreamController<int>()进行初始化,然后获取到stream对象进行listen监听。当点击按钮时通过获取sink对象,调用add方法进行事件发送,这样listen方法中就可以监听到事件响应。

StreamController<int> singleStreamController = StreamController<int>();

int num = 0;

@override
void initState() {
  // TODO: implement initState
  super.initState();
  singleStreamController.stream.listen((event) {
    setState(() {
      num = event;
    });
  });
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(),
    body: Column(
      children: [
        Text("the num is $num"),
        FlatButton(onPressed: (){
           singleStreamController.sink.add(1);
        }, child: Text("点击"))
      ],
    ),
  );
}

在上面的代码中,如果我在点击按钮时再开启一个监听的话,则会报如下错误,这就是单订阅模式的限制。

image.png

多订阅模式(广播模式)

正如广播这个名字一样,Stream也提供了broadcast方法生成可以注册多个监听器的stream。只需要将上面的初始化代码替换成这句话即可。

StreamController<int> singleStreamController = StreamController<int>.broadcast();

当我多次进行stream事件监听时,程序没有出现出任何的错误,这就是多订阅模式。

image.png

注意,在不再使用stream事件监听时要及时调用close和cancel方法进行取消订阅和事件流关闭。

StreamBuilder

上面的例子是点击界面中的按钮改变数值,通过sink发送事件,然后stream监听到数值变化再进行setState进行状态改变。其实流程还是有点绕的,Stream中也提供了可以直接在控件中获取Stream监听的写法就是StreamBuilder。如上面的代码可以写成:

StreamController<int> singleStreamController = StreamController<int>.broadcast();

@override
void initState() {
  // TODO: implement initState
  super.initState();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(),
    body: Column(
      children: [
        StreamBuilder<Object>(
          stream: singleStreamController.stream,
          builder: (context, snapshot) {
            return Text("the num is ${snapshot.data}");
          }
        ),
        FlatButton(onPressed: (){
          singleStreamController.sink.add(1);
        }, child: Text("点击"))
      ],
    ),
  );
}

其中,stream参数就是需要进行监听的数据对应的事件流对象,initialData参数是指定当stream还没有存储过数据时的默认值。builder方法中返回需要构建的控件,其中的snapshot参数为数据快照,它的data就是所存储的数据。 这样就可以在控件中监听到数据的变化,无需再调用setState方法。通过指定StreamBuilder中的stream对象,从而实现数据与界面的绑定。