1.前言

在Dart库中,有两种实现异步编程的方式(FutureStream),使用它们只需要在代码中引入dart:async即可。
本文主要介绍Stream的相关概念及利用其异步特性来实现简单的响应式编程。

2.什么是Stream?

为了将Stream的概念可视化与简单化,可以将它想成是管道(pipe)的两端,它只允许从一端插入数据并通过管道从另外一端流出数据。
在Flutter中,

  • 我们将这样的管道称作Stream
  • 为了控制Stream,我们通常可以使用StreamController来进行管理;
  • 为了向Stream中插入数据,StreamController提供了类型为StreamSink的属性sink作为入口;
  • StreamController提供stream属性作为数据的出口。

通常在本文范围内我们会使用StreamController来管理Stream,后续文章在引入rxdart这个库之后会更多的使用Subject。

3.Stream可以传输什么?

任何东西都可以!包括简单的值,事件,对象,集合,map,error或者其他的Stream,任何类型的数据都可以使用Stream来传输。

4.如何感知Stream中传输的数据?

当你需要使用Stream中传输的数据时,可以简单地使用listen函数来监听StreamController的stream属性。
在定义完listener(监听者)之后,我们会收到StreamSubscription(订阅)对象,通过这个订阅对象我们就可以接收到Stream发送数据变更的通知。
只要至少有一个活跃的监听者,Stream会创建以下事件来通知订阅对象:

  • 数据从Stream中流出;
  • Stream接收到错误信息;
  • Stream关闭。

同时,你也可以通过订阅对象来:

  • 停止监听;
  • 暂停监听;
  • 恢复监听。

5.Stream是单纯的管道?

肯定不是!Stream均可以在数据流入之前和流出之前对数据进行处理。
我们可以使用StreamTransformer来处理Stream中的数据,它具有以下特点:

  • 它是Stream中能够捕获数据流的一系列函数;
  • 可以对Stream中的数据进行处理加工;
  • 经过处理的Stream依然是Stream(链式编码的前提)。

StreamTransformer可以用于以下场景(包括但不仅限于):

  • 过滤:过滤基于条件的任何类型数据;
  • 修改:对任何类型数据进行修改;
  • 重新分组:对数据进行重新分组;
  • 向其他Stream注入数据;
  • 缓存
  • 其他基于数据的行为和操作;
  • ...

6.Stream的类型

有两种类型的Stream。

单点订阅(单播)Stream

该类型的Stream在其整个生命周期中只有一个监听者。

如果订阅已经被取消,就不能再次监听这个Stream。

多点订阅(广播)Stream

对比前者,该类型的Stream不限制监听者的数量。

我们可以在广播Stream的任何周期添加监听者,新的监听者会在监听的同时收到相应的订阅。

7.如何使用Stream流出数据构建Widget?

Flutter提供了名为StreamBuilder的类,它监听Stream,并且在Stream数据流出时会自动重新构建widget,通过其builder进行回调。
以下是示例代码:

StreamBuilder<T>(key: <#该组件的唯一ID,可选#>, stream: <#被监听的Stream#>, initialData: <#初始数据,可选#>, builder:(BuildContext context, AsyncSnapshot<T> snapshot) {
return <#实际的widget构造代码#>;
})

demo

import 'dart:async';

import 'package:flutter/material.dart';

class StreamDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('StreamDemo'),
elevation: 0.0,
),
body: StreamDemoHome(),
);
}
} class StreamDemoHome extends StatefulWidget {
@override
_StreamDemoHomeState createState() => _StreamDemoHomeState();
} class _StreamDemoHomeState extends State<StreamDemoHome> {
StreamSubscription _streamDemoSubscription;
StreamController<String> _streamDemo;
StreamSink _sinkDemo;
String _data = '...'; @override
void dispose() {
_streamDemo.close();
super.dispose();
} @override
void initState() {
super.initState(); print('Create a stream.');
// Stream<String> _streamDemo = Stream.fromFuture(fetchData());
_streamDemo = StreamController.broadcast();
_sinkDemo = _streamDemo.sink; print('Start listening on a stream.');
_streamDemoSubscription =
_streamDemo.stream.listen(onData, onError: onError, onDone: onDone); _streamDemo.stream.listen(onDataTwo, onError: onError, onDone: onDone); print('Initialize completed.');
} void onDone() {
print('Done!');
} void onError(error) {
print('Error: $error');
} void onData(String data) {
setState(() {
_data = data;
});
print('$data');
} void onDataTwo(String data) {
print('onDataTwo: $data');
} void _pauseStream() {
print('Pause subscription');
_streamDemoSubscription.pause();
} void _resumeStream() {
print('Resume subscription');
_streamDemoSubscription.resume();
} void _cancelStream() {
print('Cancel subscription');
_streamDemoSubscription.cancel();
} void _addDataToStream() async {
print('Add data to stream.'); String data = await fetchData();
// _streamDemo.add(data);
_sinkDemo.add(data);
} Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 5));
// throw 'Something happened';
return 'hello ~';
} @override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Text(_data),
StreamBuilder(
stream: _streamDemo.stream,
initialData: '...',
builder: (context, snapshot) {
return Text('${snapshot.data}');
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
child: Text('Add'),
onPressed: _addDataToStream,
),
FlatButton(
child: Text('Pause'),
onPressed: _pauseStream,
),
FlatButton(
child: Text('Resume'),
onPressed: _resumeStream,
),
FlatButton(
child: Text('Cancel'),
onPressed: _cancelStream,
),
],
),
],
),
),
);
}
}

效果:

Flutter响应式编程 - Stream的更多相关文章

  1. Flutter响应式编程 - RxDart

    import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; import 'dart:async'; cl ...

  2. 响应式编程(Reactive Programming)(Rx)介绍

    很明显你是有兴趣学习这种被称作响应式编程的新技术才来看这篇文章的. 学习响应式编程是很困难的一个过程,特别是在缺乏优秀资料的前提下.刚开始学习时,我试过去找一些教程,并找到了为数不多的实用教程,但是它 ...

  3. Java9第四篇-Reactive Stream API响应式编程

    我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...

  4. Unity基于响应式编程(Reactive programming)入门

    系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoT ...

  5. [译] Swift 的响应式编程

    原文  https://github.com/bboyfeiyu/iOS-tech-frontier/blob/master/issue-3/Swift的响应式编程.md 原文链接 : Reactiv ...

  6. [iOS] 响应式编程开发-ReactiveCocoa(一)

    什么是响应式编程 响应式编程是一种面向数据流和变化传播的编程范式.这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播. 例如,在命令式编程环境中 ...

  7. springboot2 webflux 响应式编程学习路径

    springboot2 已经发布,其中最亮眼的非webflux响应式编程莫属了!响应式的weblfux可以支持高吞吐量,意味着使用相同的资源可以处理更加多的请求,毫无疑问将会成为未来技术的趋势,是必学 ...

  8. 响应式编程知多少 | Rx.NET 了解下

    1. 引言 An API for asynchronous programming with observable streams. ReactiveX is a combination of the ...

  9. [转]springboot2 webflux 响应式编程学习路径

    原文链接 spring官方文档 springboot2 已经发布,其中最亮眼的非webflux响应式编程莫属了!响应式的weblfux可以支持高吞吐量,意味着使用相同的资源可以处理更加多的请求,毫无疑 ...

随机推荐

  1. 【实战1】记一次提至administrator权限历程

    本文首发于先知社区 https://xz.aliyun.com/t/5080 前言:这是一次挖掘cms通用漏洞时发现的网站,技术含量虽然不是很高,但是也拿出来和大家分享一下吧,希望能给一部分人带来收获 ...

  2. 51nod 2517 最少01翻转次数

    小b有一个01序列,她每次可以翻转一个元素,即将该元素异或上1. 现在她希望序列不降,求最少翻转次数. 收起   输入 第一行输入一个数n,其中1≤n≤20000: 第二行输入一个由‘0’和‘1’组成 ...

  3. RestTemplate将字符串以文件的方式上传

    背景 在java后台将内容拼接为字符串,然后使用RestTemplate将字符串以文件的方式上传到指定接口 思路 使用 RestTemplate 时,将字符串封装为字节流,然后上传 代码 /** * ...

  4. python链接oracle数据库以及数据库的增删改查实例

    初次使用python链接oracle,所以想记录下我遇到的问题,便于向我这样初次尝试的朋友能够快速的配置好环境进入开发环节. 1.首先,python链接oracle数据库需要配置好环境. 我的相关环境 ...

  5. 2019/10/22 test T1 题解

    题目描述 给定n个a[i],b[i],求min(x$\in$R){$\sum\limits_{i=1}^{n}$|a[i]*x+b[i]|} 输入格式 第 1行 1个整数 n第 2行 n个整数,第 i ...

  6. Oracle 重新编译存储过程/函数等

    第一种  如果你使用 PL/SQL Developer工具          左侧工具栏中选择“存储过程”->选择已经失效的procedure->右键->选择重新编译 即可完成 第二 ...

  7. GreenPlum 常用命令

    gpstate 命令 参数 作用 gpstate -b => 显示简要状态 gpstate -c => 显示主镜像映射 gpstart -d => 指定数据目录(默认值:$MASTE ...

  8. UOJ#397. 【NOI2018】情报中心 线段树合并 虚树

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ397.com 前言 这真可做吗?只能贺题解啊-- 题解 我们称一条路径的 LCA 为这条路径两端点的 LCA. 我们将相交 ...

  9. SpringCloud:搭建微服务项目框架 microservicecloud

    1.搭建整体父工程 microservicecloud 新建父工程microservicecloud,切记是Packageing是pom模式 主要是定义POM文件,将后续各个子模块公用的jar包等统一 ...

  10. css+vue实现流程图

    主要用css+flex布局实现样式部分,vue实现组件逻辑.首先看下效果吧: 当空间不够时还可以使用拖拽功能 接下来说明下实现思路 1.首先是实现单个节点样式,这个很简单不谈了,节点后都跟有一小段连接 ...