Flutter----Stream讲解

Stream

StreamFlutter中也是属于非常关键的概念,就好比前面我有写过一篇event_bus的文章,我就是用Stream去做的,有兴趣的可以去前面看看。

Flutter中,状态管理出了本身自带的InheritedWidget之外,还有什么rxdart,Bolc,redux,provider以及别的大佬们写的插件。但是不管是什么状态管理的模式,我看到的基本都是离不开Stream的封装。

通俗的来讲,这个Stream就是事件流或者管道,事件流大家都不会太陌生吧,简单的来说就是:基于事件流驱动设计代码,类似于event_bus,先注册ranho-u再监听订约时间,并且针对事件变换处理不同的响应。

我有幸看过一篇文章,但是忘了是哪位大佬的了,在Flutter中,既然我们谈到了Stream,那么我们自然就不能丢掉Stream的“四大天王”:StreamController,Sink,Stream,StreamSubscription。

好了不多说了,我们就开始进入正题,慢慢的去了解Stream是怎么去进行操作的。

Stream

最简单的,我们都知道,我们在使用任何东西的同时,一定要先定义吧,然后创建注册和监听。

class StreamDemoPage extends StatefulWidget {
  @override
  _StreamDemoPageState createState() => _StreamDemoPageState();
}

class _StreamDemoPageState extends State<StreamDemoPage> {

  void onData(String data){  //data参数表示的就是_streamDemo这个Stream出来的数据
    print('$data');
  }

  Future<String> fetchData() async{
    await Future.delayed(Duration(seconds: 2));  //延迟2s获取数据
    return "我是Stream中的数据";
  }

  @override
  void initState() {
    super.initState();
    //创建Stream
    print('创建一个Stream');
    Stream<String> _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<StreamDemoPage> {

  void onData(String data){  //data参数表示的就是_streamDemo这个Stream出来的数据
    print('$data');
  }

  void onDone(){
    print('-----Done-----!');
  }

  void onError(error){
    print('Error: $error');
  }


  Future<String> fetchData() async{
    await Future.delayed(Duration(seconds: 2));//延迟3秒
    return "我是Stream中的数据";
  }

  @override
  void initState() {
    super.initState();
    //创建Stream
    print('创建一个Stream');
    Stream<String> _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<StreamDemoPage> {
  StreamSubscription _streamSubscription;

  void onDone() {
    print('-----Done-----!');
  }

  void onError(error) {
    print('Error: $error');
  }

  void onData(String data) {
    //data参数表示的就是_streamDemo这个Stream出来的数据
    print('$data');
  }

  Future<String> 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<String> _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: <Widget>[
            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

为了进一步的去了解这个过程,我们使用StreamController进行管理和控制Stream

场景: 我们先按一下add按钮,然后会输出数据,在按下pause按钮,会发现我们无论怎么去按add都没有输出数据,最后我们按下resume按钮才会有数据。

进一步修改代码:

class _StreamDemoPageState extends State<StreamDemoPage> {
  StreamSubscription _streamSubscription;
  StreamController<String> _streamController;

  void onDone() {
    print('-----Done-----!');
  }

  void onError(error) {
    print('Error: $error');
  }

  void onData(String data) {
    //data参数表示的就是_streamDemo这个Stream出来的数据
    print('$data');
  }

  Future<String> 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<String> _streamDemo = Stream.fromFuture(fetchData());
    _streamController = new StreamController<String>();  //这里去实例化

    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: <Widget>[
            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

我们可以使用SinkStream上添加数据

修改后的代码:

class _StreamDemoPageState extends State<StreamDemoPage> {
  StreamSubscription _streamSubscription;
  StreamController<String> _streamController;
  StreamSink _streamSink;

  void onDone() {
    print('-----Done-----!');
  }

  void onError(error) {
    print('Error: $error');
  }

  void onData(String data) {
    //data参数表示的就是_streamDemo这个Stream出来的数据
    print('$data');
  }

  Future<String> 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<String> _streamDemo = Stream.fromFuture(fetchData());
    _streamController = new StreamController<String>();  //这里去实例化
    _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: <Widget>[
            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** 那么相信你对于槽信号这边应该不难理解。如果你实现是很难理解就当做成一个订阅者发布者来理解就好了!

StreamSubscription

感觉他应该也说完了,在上面都有这个Subscription插入,有兴趣的还是想了解了可以去看一下源码,如果实在不想看源码就可以看看我前面写的event_bus广播的文章。

补充:StringBuilder

StringBuilder是根据Stream上的数据去构建WIdgetStream上的数据变化时,他会自己去重构Widget不需要我们去setState()

好了不说什么了修改代码:

class _StreamDemoPageState extends State<StreamDemoPage> {
  StreamSubscription _streamSubscription;
  StreamController<String> _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<String> 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<String> _streamDemo = Stream.fromFuture(fetchData());
    _streamController = new StreamController<String>();  //这里去实例化
    _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: <Widget>[
            StreamBuilder(
              stream: _streamController.stream,
              initialData: _data,
              builder: (context,snapshot){
                return Text('${snapshot.data}');
              },
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                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的使用!

我们然后看一下控制台的打印:

就这样完成了!!!!其余三个按钮的功能相信我不说你也能猜到了,如果自己很想知道可以自己去试一下代码复制下来就好。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值