Flutter中stream学习

Flutter中stream学习

  • 概述
  • Stream的基础概念
  • stream的常用方法
    • Stream.fromFuture(Future<T> future)
    • Stream.fromFutures(Iterable<Future<T>> futures)
    • Stream.fromIterable(Iterable<T> elements)
    • Stream.periodic(Duration period, [T computation(int computationCount)?])
    • Stream<T> take(int count)
    • Stream<T> takeWhile(bool test(T element))
    • Stream<T> where(bool test(T event))
    • Stream<T> distinct([bool equals(T previous, T next)])
    • Stream<T> skip(int count)
    • Stream<T> skipWhile(bool test(T element))
    • Stream<S> map<S>(S convert(T event))
    • Stream<S> expand<S>(Iterable<S> convert(T element))
  • Stream的分类
    • 单订阅
    • 广播订阅

概述

Stream 主要应用于 Flutter 的异步操作,在其他编程语言中也存在;Stream 提供了一种接受事件队列的方法,可通过 listen 进行数据监听,通过 error 接收失败状态,通过 done 来接收结束状态;

Stream的基础概念

  • Stream:表示一个可以接收异步事件的数据源。可以生成一个或多个值。
  • StreamController:控制Stream,可以向其添加事件、错误以及关闭它。
  • StreamSubscription:表示对Stream的监听,可以用来取消订阅。
  • Sink:用来向Stream添加数据、错误、以及关闭。

stream的常用方法

Stream.fromFuture(Future future)

Stream通过Future对象创建新的单订阅流, 当Future对象完成时会触发 data / error, 然后已done事件结束

Future<String> getDate() async {await Future.delayed(const Duration(seconds: 3));return "当前时间为${DateTime.now()}";}void testStreamFromFuture() {Stream.fromFuture(getDate()).listen((event) {print("testStreamFromFuture============$event");}).onDone(() {print("testStreamFromFuture==========done 结束");});}

输出结果:
在这里插入图片描述

Stream.fromFutures(Iterable<Future> futures)

Stream 通过一系列的 Future 创建新的单订阅流,每个 Future 都会有自身的 data / error 事件, 当这一系列的 Future 均完成时,Stream 以 done 事件结束;若 Futures 为空,则 Stream 会立刻关闭;其分析源码,很直接的看到是将每一个 Future 事件监听完之后才会执行的微事件结束;

源码代码:

factory Stream.fromFutures(Iterable<Future<T>> futures) {_StreamController<T> controller =new _SyncStreamController<T>(null, null, null, null);int count = 0;// Declare these as variables holding closures instead of as// function declarations.// This avoids creating a new closure from the functions for each future.void onValue(T value) {if (!controller.isClosed) {controller._add(value);if (--count == 0) controller._closeUnchecked();}}void onError(Object error, StackTrace stack) {if (!controller.isClosed) {controller._addError(error, stack);if (--count == 0) controller._closeUnchecked();}}// The futures are already running, so start listening to them immediately// (instead of waiting for the stream to be listened on).// If we wait, we might not catch errors in the futures in time.for (var future in futures) {count++;future.then(onValue, onError: onError);}// Use schedule microtask since controller is sync.if (count == 0) scheduleMicrotask(controller.close);return controller.stream;}

示例代码:

var datas = [getDate(), getDate(), getDate()];Stream.fromFutures(datas).listen((event) {print("testStreamFromFutures============$event");}).onDone(() {print("testStreamFromFutures==========done 结束");});

输出结果:
在这里插入图片描述

Stream.fromIterable(Iterable elements)

Stream 通过数据集合中获取并创建单订阅流,通过 listen 监听迭代器中每一个子 element,当 Stream 监听到取消订阅或 Iterator.moveNext 返回 false / throw 异常 时停止迭代;

void testStreamFromIterable() {var datas = [1, 2, "5.toStroing", false, 9];Stream.fromIterable(datas).listen((event) {print("testStreamFromIterable============$event");}).onDone(() {print("testStreamFromIterable==========done 结束");});}

输出结果:
在这里插入图片描述

Stream.periodic(Duration period, [T computation(int computationCount)?])

Stream 通过 Duration 对象作为参数创建一个周期性事件流,其中若不设置 computation 时 onData 获取数据为 null;若没有事件结束则会一直周期性执行; 因为 computation 函数是返回流的结果

void testStreamPeriodic() {Stream.periodic(const Duration(seconds: 1)).listen((event) {print("testStreamPeriodic===没有computation==================$event");});Stream.periodic(const Duration(seconds: 1), (x) => x).listen((event) {print("testStreamPeriodic---- listen========$event");}).onDone(() {print("testStreamPeriodic==========done 结束");});}

输出结果:
在这里插入图片描述

Stream take(int count)

take() 对于单订阅方式,可以提供 take 设置之前的 Stream 订阅数据,例如设置中断 Stream.periodic 周期展示次数;小菜粗略理解为 take 可以作为中断订阅, 如果 take 设置次数大于 onDone 之前的订阅数据次数,Stream 依旧获取所有 onDone 之前的订阅数据

void testStreamTake() {Stream.periodic(const Duration(seconds: 1), (x) => x).take(5) // 如果不设置这个, 这个流将一直会执行, 但是设置之后只会执行设置的数的次数.listen((event) {print("testStreamTake===========$event");}).onDone(() {print("testStreamTake==============done 结束");});}

输出结果:
在这里插入图片描述

Stream takeWhile(bool test(T element))

takeWhile 也可以实现上述take方法相同效果, 返回一个 boolean 类型,如果为 false 则中断订阅

void testStreamTakeWhile() {Duration interval = const Duration(seconds: 1);Stream<int> streamData = Stream<int>.periodic(interval, (data) => data);streamData.takeWhile((element) {print('Stream.periodic.takeWhile -> $element');return element < 5;}).listen((event) {print('Stream.periodic -> $event');}).onDone(() {print('Stream.periodic -> done 结束');});}

输出结果:
在这里插入图片描述

Stream where(bool test(T event))

where 用于在当前 Stream 中创建一个新的 Stream 用来丢弃不符合 test 的数据;简单理解为类似数据库查询一样,仅过滤符合需求的数据流;且 where 可以设置多次

void testStreamWhere() {Stream.periodic(const Duration(seconds: 1), (data) => data).takeWhile((element) => element <= 5).where((event) {print('Stream.periodic.where -> $event');return event > 3;}).listen((event) {print("testStreamWhere==================$event");}).onDone(() {print("testStreamWhere===================== done 结束");});}

输出结果:
在这里插入图片描述

Stream distinct([bool equals(T previous, T next)])

作用:相邻的两个数据去重哈

void testStreamDistinct() {var datas = [1, 2, '3.toString()', true, true, false, true, 6];Stream.fromIterable(datas).distinct().listen((event) {print("testStreamDistinct===========================$event");}).onDone(() {print('testStreamDistinct============================ done 结束');});}

输出结果:
在这里插入图片描述

Stream skip(int count)

作用: skip 用于跳过符合条件的订阅数据次数 count: 跳过的次数;

void testStreamSkip() {Stream<int> streamData =Stream<int>.periodic(const Duration(seconds: 1), (data) => data + 1);streamData.takeWhile((element) {print('Stream.periodic.takeWhile -> $element');return element <= 6;}).where((event) {print('Stream.periodic.where -> $event');return event > 2;}).skip(2).listen((event) {print('Stream.periodic -> $event');}).onDone(() {print('Stream.periodic -> done 结束');});}

输出结果 :
在这里插入图片描述

Stream skipWhile(bool test(T element))

skipWhile 用于跳过在 where 符合条件下满足设置 条件的订阅数据;即当 返回 为 true 时跳过当前订阅数据监听;

void testSkipWhile() {Stream.periodic(const Duration(seconds: 1), (data) => data).takeWhile((element) => element < 5).skipWhile((element) => element < 3).listen((event) {print("testSkipWhile=========$event");}).onDone(() {print("testSkipWhile========done 结束");});}

输出 结果:
在这里插入图片描述

Stream map(S convert(T event))

在当前 Stream 基础上创建一个新的 Stream 并对当前 Stream 进行数据操作,onData 监听到的是 map 变更后的新的数据流;

void testStreamMap() {// 创还能一个stream刘Stream<int> streamData =Stream<int>.periodic(const Duration(seconds: 1), (data) => data + 1);streamData.takeWhile((element) {print('Stream.periodic.takeWhile -> $element');return element < 5;}).map((event) {print('Stream.periodic.map -> $event -> ${event * 100}');return event * 100;}).listen((event) {print('Stream.periodic -> $event');}).onDone(() {print('Stream.periodic -> done 结束');});}

输出结果:
在这里插入图片描述

Stream expand(Iterable convert(T element))

在当前 Stream 基础上创建新的 Stream 并将当前订阅数据转为新的订阅数据组,onData 监听 数据组 中每个新的订阅数据元素;

void testStreamExpand() {Stream<int> streamData =Stream<int>.periodic(const Duration(seconds: 1), (data) => data + 1);streamData.takeWhile((element) {print('Stream.periodic.takeWhile -> $element');return element <= 6;}).expand((element) {print('Stream.periodic.expand -> $element -> ${element * 10} -> ${element * 100}');return [element, element * 10, element * 100];}).listen((event) {print('Stream.periodic -> $event');}).onDone(() {print('Stream.periodic -> done 结束');});}

输出结果:
在这里插入图片描述

Stream的分类

单订阅

默认情况下Streams会被设置成单订阅,点订阅会保持当前的值,直到有其它的订阅。

单订阅Stream(Single-Subscription Stream)一次只能有一个监听器(listener),当我们对单订阅进行监听的时候,程序会被错。通常用于一次性事件

void testStreamController() {// 使用streamController创建一个streamfinal streamController = StreamController<int>();// 获取stream流final stream = streamController.stream;// 监听streamstream.listen((event) {print("testStreamController========================$event");});// stream.listen((event) {//   print("testStreamController=11111=======================$event");// });// 添加测试数据到streamstreamController.sink.add(1);streamController.sink.add(2);streamController.sink.add(3);// 关闭stream流streamController.close();}

如果我打开上述注释掉的监听, 对一个单订阅的stream进行多次监听会报如下错误:
在这里插入图片描述

广播订阅

广播(Broadcast Stream)允许多个监听器,可以同时向多个订阅者推送数据。 这种类型适合用于事件广播,比如用户操作、全局数据推送等。

void testStreamBoardcast() {final streamController = StreamController<int>.broadcast();final stream = streamController.stream;stream.listen((event) {print("testStreamBoardcast==============$event");});stream.listen((event) {print("testStreamBoardcast111111111===================$event");});streamController.sink.add(1);streamController.sink.add(2);streamController.sink.add(3);streamController.close();}

输出结果:
在这里插入图片描述

除此之外Flutter官方还提供了StreamBuilder这种专门用于监听Stream并根据数据变化更新UI的Widget。具体用法可以参考官方文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31786.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络:计算机网络的组成和功能

计算机网络的组成&#xff1a; 计算机网络的工作方式&#xff1a; 计算机网络的逻辑功能; 总结&#xff1a; 计算机网络的功能&#xff1a; 1.数据通信 2.资源共享 3.分布式处理:计算机网络的分布式处理是指将计算任务分散到网络中的多个节点&#xff08;计算机或设备&…

【redis】五种数据类型和编码方式

文章目录 五种数据类型编码方式stringhashlistsetzset查询内部编码 五种数据类型 字符串&#xff1a;Java 中的 String哈希&#xff1a;Java 中的 HashMap列表&#xff1a;Java 中的 List集合&#xff1a;Java 中的 Set有序集合&#xff1a;除了存 member 之外&#xff0c;还有…

AI与现有运维管理软件之间的相互影响和协同发展

AI与现有运维软件之间的相互影响和协同发展。我们可以从几个角度来探讨&#xff1a; 1. AI与运维软件的共生关系 已有运维软件作为大模型的一部分&#xff1a;运维软件&#xff08;如监控易&#xff09;在长期运行中积累了大量的数据和经验&#xff0c;这些数据和经验可以被用来…

深度评测DeepSeek、ChatGPT O1和谷歌Gemini AI应用开发场景 - DeepSeek性能完胜!

下面我会展示我为期一周的实验结果&#xff0c;创作不宜&#xff0c;希望大家关注我&#xff0c;以后多多互3&#xff01;前一阵我在互联网上看到很多关于DeepSeek R1的讨论&#xff0c;这个开源模型据说可以媲美&#xff0c;甚至优于像OpenAI o1这样的付费模型。 由于我在日常…

使用DeepSeek+蓝耘快速设计网页简易版《我的世界》小游戏

前言&#xff1a;如今&#xff0c;借助先进的人工智能模型与便捷的云平台&#xff0c;即便是新手开发者&#xff0c;也能开启创意游戏的设计之旅。DeepSeek 作为前沿的人工智能模型&#xff0c;具备强大的功能与潜力&#xff0c;而蓝耘智算云平台则为其提供了稳定高效的运行环境…

Hcaptcha验证码自动识别方案详解

Hcaptcha验证系统简介 这个令人头疼的验证系统长这样: 还有这样: 看着就让人不开心,每次都要玩这种小游戏。 工作原理 1. 环境评估(形式主义阶段) 它会检查这些东西: 浏览器指纹行为数据IP地址网站设置 如果你看起来像个"正常访客",它可能就放你过。但要是发现…

SpringBoot事务管理

Spring事务管理 在日常开发过程中&#xff0c;只要涉及数据操作&#xff0c;都不可避免地会涉及事务管理相关内容&#xff0c;而Spring提供了强大的事务管理机制&#xff0c;能够帮助开发者更轻松地处理数据一致性和事务的问题。 一、什么是事务管理 事务&#xff08;Transa…

JU TPS研究笔记

这个模板的Cover Demo和尘白禁区一样&#xff0c;是自由观察和背后锁定视角可切换的TPS。这种模式比单独做自由观察或背后锁定都要复杂。在非瞄准也就是自由观察状态&#xff0c;鼠标控制相机转动&#xff0c;WASD控制人物以相机前方为前方一边移动一边平滑旋转到面对移动方向。…

[NewStarCTF 2023 公开赛道]ez_sql1 【sqlmap使用/大小写绕过】

题目&#xff1a; 发现id处可以sql注入&#xff1a; 虽然输入id1;show databases;#没什么回显&#xff0c;但是知道这里是字符型注入了 这次利用sqlmap注入 --dbs&#xff1a;列出所有数据库名字 python .\sqlmap.py -u http://a40b2f0a-823f-4c99-b43c-08b94ed0abb2.node5.…

DeepSeek-实用集成大礼包

随着DeepSeek的持续火热,在各种平台看到大家基于deepseek+各类应用的案例。这些案例真假难辨,现在DeepSeek已经推出了官方的Awesome DeepSeek Integrations,集成了各类应用,下面是详细的介绍。 DeepSeek Integrations 是 DeepSeek 官方在 GitHub 上精心整理的一个集合了各种…

Ubuntu安装问题汇总

参考文章&#xff1a; 【Ubuntu常用快捷键总结】 【王道Python常用软件安装指引】 1. 无法连接虚拟设备 sat0:0 【问题】&#xff1a;出现下图所示弹框。 【问题解决】&#xff1a; 点击 “否” 。 点击左上角的 “虚拟机” → “设置…” → “CD/DVD (SATA)” &#xff0c;…

深陷帕金森困境,怎样重燃生活信心?

帕金森&#xff0c;这个悄然影响无数中老年人生活的神经系统疾病&#xff0c;正逐渐走进大众视野。患病后&#xff0c;患者常出现静止性震颤&#xff0c;安静时手部、下肢不自主抖动&#xff0c;如同在默默诉说着身体的异常。肢体变得僵硬&#xff0c;行动迟缓&#xff0c;起步…

空间遥感智能处理技术发展现状与趋势

在数字化时代&#xff0c;空间遥感技术已经成为获取地球表面信息的重要手段。随着卫星遥感技术的快速发展&#xff0c;获取的遥感数据量激增&#xff0c;这对遥感数据的智能处理提出了更高的要求。本文将探讨空间遥感智能处理技术的发展现状与未来趋势。 发展现状 大数据与人工…

svn删除所有隐藏.svn文件,文件夹脱离svn控制

新建一个文件&#xff0c;取名remove-svn-folders.reg&#xff0c;输入如下内容&#xff1a; Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN] "Delete SVN Folders" [HKEY_LOCAL_MACHINE\SOFTWARE\Class…

Datawhale coze-ai-assistant 笔记2

目录 快速搭建一个 AI 助手智能体 搭建步骤 步骤1&#xff1a;创建一个智能体 步骤2&#xff1a;编写提示词 步骤3&#xff1a;调试智能体 步骤4&#xff1a;发布智能体 设置智能体模型 选择模型 生成多样性 输入及输出设置 如何使用 步骤1&#xff1a;更换模型 步…

win10电脑鼠标速度突然变的很慢?

电脑鼠标突然变很慢&#xff0c;杀毒检测后没问题&#xff0c;鼠标设置也没变&#xff0c;最后发现可能是误触鼠标的“DPI”调节键。 DPI调节键在鼠标滚轮下方&#xff0c;再次点击即可恢复正常鼠标速度。 如果有和-的按键&#xff0c;速度变快&#xff0c;-速度变慢。 图源&…

若依RuoYi-Cloud-Plus微服务版(完整版)前后端部署

一.目标 在浏览器上成功登录进入 二.源码下载 后端源码&#xff1a;前往Gitee下载页面(https://gitee.com/dromara/RuoYi-Cloud-Plus)下载解压到工作目录。 前端源码&#xff1a; 前往Gitee下载页面(https://gitee.com/JavaLionLi/plus-ui)下载解压到工作目录。 文档地址&a…

vue3+elementuiplus的table表格动态高度

table表格流体高度 1、前提 了解自定义指令、hooks 2、核心思路 通过自定义指令&#xff08;new ResizeObserver&#xff09;监听表格变化&#xff0c;然后通过hooks去更新表格高度。 3、核心代码 src/directives/resize.ts // import { debounce } from /utils;import { t…

Django与数据库

我叫补三补四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲alpha策略制定后的测试问题 mysql配置 Django模型体现了面向对象的编程技术&#xff0c;是一种面向对象的编程语言和不兼容类型能相互转化的编程技术&#xff0c;这种技术也叫ORM&#…

VMware下载安装Ubuntu详解

一、Linux简介 1、不同领域的主流操作系统 桌面操作系统服务器操作系统移动设备操作系统嵌入式操作系统 1.1、桌面操作系统 Windows&#xff08;用户数量最多&#xff09;Mac OS&#xff08;苹果电脑操作系统&#xff09;Linux&#xff08;用户数量少&#xff09; 1.2、服…