flutter:Future、Stream、RxDart

news2024/9/24 7:24:58

Future

在Flutter中,Future是Dart语言中的一个类,用于表示异步操作的结果。与Future相关的的重要关键字包括async和await。

  • async:这个关键字用于在方法或函数声明前添加,以指示该方法为异步方法。在异步方法中,执行顺序可以是非阻塞的,不会阻塞当前线程。
  • await:这个关键字用于在异步方法中等待并获取异步表达式的执行结果。它只能在async修饰的方法中使用。
class _MyHomePageState extends State<MyHomePage> {
  String string = '';
  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async{
              print("开始获取数据");
             await fetchData();
              print(string);
            },
            child: Text("获取数据"),
          ),
        ));
  }

  Future fetchData() async {
    await Future.delayed(const Duration(seconds: 2));
    string = '数据获取完成';
  }
}

在这里插入图片描述

Stream

在Flutter中,Stream(流)是一种用于处理异步事件序列的概念。它常见的应用包括:

  • 异步数据获取:Stream常用于从服务器或本地数据库等异步源获取数据。你可以使用Stream来监听数据源的变化,并在数据可用时进行响应。

  • 状态管理:Stream可以用作应用程序的状态管理工具。你可以将应用程序中的状态封装成一个Stream,通过监听该Stream来更新用户界面。例如,你可以在一个Stream中存储应用的登录状态,并在登录状态发生变化时通知界面进行相应的UI更新。

  • 事件总线:Stream可以用作事件总线,用于在应用程序的不同部分传递事件和数据。你可以创建一个全局的Stream,订阅者可以监听该Stream并接收事件。这样可以实现不同组件之间的解耦和通信。

  • 用户输入:当处理用户输入时,Stream也很有用。你可以使用Stream来监听用户在应用程序中的各种操作,例如点击按钮、滑动屏幕等。通过将用户输入转化为Stream事件,你可以将应用程序与用户交互关联起来。

  • 文件读写:Stream还可以用于处理文件读写操作。你可以通过Stream来读取和写入文件,以便异步处理大型文件或流式传输数据。

在Flutter中,使用Dart的Stream类来创建和管理Stream。你可以使用StreamController来控制Stream的创建、数据添加和Stream关闭操作。另外,Flutter还提供了许多Stream相关的操作符和方法,如mapwheretransform等,用于流的转换和处理。

异步数据获取

class _MyHomePageState extends State<MyHomePage> {
  // 创建控制器
  final StreamController _streamController = StreamController();

  
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  fetchData();
                },
                child: const Text("获取数据")),
            StreamBuilder(
                stream: _streamController.stream,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return Text('异步数据:${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('发生错误:${snapshot.error}');
                  } else {
                    return const Text('加载中...');
                  }
                })
          ],
        ));
  }

  fetchData() async {
    await Future.delayed(const Duration(seconds: 1));
    _streamController.sink.add(1); // 发送第一个值
    await Future.delayed(const Duration(seconds: 2));
    _streamController.sink.add(2); // 发送第二个值
  }
}

在这里插入图片描述

使用Stream来处理异步和使用async/await来处理异步有几个区别,包括:

  1. 控制流:使用Stream时,可以通过监听数据流的事件来处理异步操作的结果。当新的数据到达时,可以执行相应的逻辑。而使用async/await时,代码会在等待异步操作完成后继续执行,顺序执行。

  2. 数据处理:使用Stream可以处理多个值或者一系列值的异步操作,例如数据流、事件流等。而使用async/await一次只能处理一个异步操作的结果。

  3. 使用场景:Stream适用于需要处理持续产生数据的异步操作,例如网络请求、传感器数据等。而async/await适用于一次性获取结果的异步操作,例如读取文件、等待用户输入等。

  4. 代码结构:使用Stream时,需要创建StreamController并手动管理数据的发送和订阅。而使用async/await时,可以直接在异步函数中使用关键字await来等待异步操作的结果,代码更加简洁。

总的来说,Stream更适用于处理连续产生数据的异步操作,并且可以方便地对数据流进行处理和转换。而async/await更适用于一次性获取结果的异步操作,代码结构更加简单明了。具体使用哪种方式取决于你的需求和代码结构的复杂度。在某些情况下,两种方式也可以结合使用,例如使用async/await等待一个Future的结果,并将其转换为Stream进行后续处理。

状态管理

上面那个例子也可以看做状态管理,当某一个状态发生改变后,Stream会监听到,然后根据新的状态来更新视图。

那与Provider有什么区别呢?我觉得最重要的区别就是使用Provider时状态可以被存储起来,而Stream不会存储起来。基于此可以来确定需要使用哪一个。

事件总线

import 'dart:async';

class EventBus {
  static final EventBus _instance = EventBus._internal();

  factory EventBus() => _instance;

  EventBus._internal();
  // 使用 broadcast() 方法创建了一个可以实时广播事件的 StreamController
  final _controller = StreamController<dynamic>.broadcast();
  
  Stream get stream => _controller.stream;

  void fire(dynamic event) {
    _controller.sink.add(event);
  }

  void dispose() {
    _controller.close();
  }
}
// 订阅事件
EventBus().stream.listen((event) {
  // 处理事件
  print('Received event: $event');
});

// 发送事件
EventBus().fire('Event data');

用户输入

没太明白,好像也没什么用

文件读取

class _MyHomePageState extends State<MyHomePage> {
  // 创建控制器
  final StreamController _streamController = StreamController<String>();

  
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  fetchData();
                },
                child: const Text("获取数据")),
            const SizedBox(
              height: 30,
            ),
            StreamBuilder(
                stream: _streamController.stream,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return Text('异步数据:${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('发生错误:${snapshot.error}');
                  } else {
                    return const Text('加载中...');
                  }
                })
          ],
        ));
  }

  fetchData() async {
    // 文件是对文件系统上文件的引用,因此不能使用文件从资产中读取文件。您不能逐个文件访问资产文件
    File file = File('a.txt');

    Stream<String> fileStream = file
        .openRead()
        .transform(utf8.decoder) // 解码
        .transform(const LineSplitter()); // 将内容按行切分

    fileStream.listen((String line) {
      // 发送读取到的内容到Stream
      _streamController.add(line);
    }, onDone: () {
      // 文件读取完成,关闭流
      _streamController.close();
    }, onError: (error) {
      // 发送错误事件到Stream
      _streamController.addError(error);
    });
  }
}

大概就是这样,但是你无法读取到项目下的文件。
要么使用path_provider来获取路径;要么将文件变成静态文件,但是变成静态文件后要使用rootBundle.loadString进行读取

优点

  • 使用Stream来处理文件时,可以更方便的处理大量数据,不必一次性将整个文件加载到内存中,这对于处理大型文件或实时数据流非常有用。
  • 使用Stream时,文件读取过程中会触发各种事件,例如数据可用、读取完成或发生错误等。你可以通过监听这些事件来采取适当的行动,如更新UI或处理错误。

常用方法

这个可以根据需要自行百度具体用法

  • map: 将数据流中的每个事件转换为一个新的事件。例如,可以使用map方法将数据流中的每个数字加倍。

  • where: 根据给定的条件过滤数据流中的事件。例如,可以使用where方法过滤出数据流中的偶数。

  • expand: 将每个事件转换为多个事件,并将它们展平成一个数据流。例如,可以使用expand方法将每个字符串事件拆分为单个字符事件。

  • take: 仅从数据流中获取前n个事件。例如,可以使用take方法获取前5个事件。

  • skip: 跳过数据流中的前n个事件,然后开始接收后续的事件。例如,可以使用skip方法跳过前3个事件。

  • distinct: 过滤掉数据流中重复的事件。例如,可以使用distinct方法过滤掉重复的字符串事件。

  • merge: 将多个数据流合并为一个数据流。例如,可以使用merge方法将两个整数数据流合并为一个整数数据流。

  • zip: 将两个数据流中的事件一一配对,并将它们合并为一个新的事件。例如,可以使用zip方法将一个字符串数据流和一个整数数据流配对为一个新的数据流

RxDart

RxDart是基于Dart的响应式编程库,提供了对Stream的扩展和增强。一般情况下使用Dart内置的Stream是完全足够的,这里只简单了解一下,感兴趣的可以自行查看文档

如何选择

使用Stream时:

  • 简单的异步操作:如果你只需要处理简单的异步操作,例如监听网络请求结果、处理用户输入事件等,使用Stream就足够了。Stream提供了基本的异步编程机制,可以满足大多数的需求。
  • 较少的数据转换和处理:如果你不需要复杂的数据转换和处理操作,只需要监听数据流的变化,并进行一些简单的操作,如过滤、排序等,那么使用Stream就足够了。

使用RxDart时:

  • 复杂的数据处理:如果你需要进行复杂的数据处理和转换操作,如数据映射、过滤、组合、扁平化等,RxDart提供了丰富的操作符和功能,能够极大地简化代码和提高开发效率。
  • 响应式需求:如果你需要实现响应式编程的思想,即将数据流分成多个阶段进行处理,并对每个阶段的数据进行监听和反应,RxDart非常适合。它提供的Observable对象和操作符能够帮助你构建响应式的数据流处理链。
  • 错误处理:RxDart提供了更便捷的错误处理机制,通过onError()操作符可以方便地捕获和处理异常,使得错误处理更加灵活和高效。

官方文档
https://pub-web.flutter-io.cn/packages/rxdart

安装

flutter pub add rxdart

异步数据获取

class _MyHomePageState extends State<MyHomePage> {
  // 创建控制器
  final BehaviorSubject<int> _streamController = BehaviorSubject<int>();

  
  void dispose() {
    super.dispose();
    _streamController.close();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Column(
          children: [
            ElevatedButton(
                onPressed: () {
                  fetchData();
                },
                child: const Text("获取数据")),
            StreamBuilder(
                stream: _streamController.stream,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return Text('异步数据:${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('发生错误:${snapshot.error}');
                  } else {
                    return const Text('加载中...');
                  }
                })
          ],
        ));
  }

  fetchData() async {
    await Future.delayed(const Duration(seconds: 1));
    _streamController.add(1); // 发送第一个值
    await Future.delayed(const Duration(seconds: 2));
    _streamController.add(2); // 发送第二个值
  }
}

文件读取

使用File类来打开要读取的文件。

final file = File('data.txt');

使用Observable来创建一个可观察的流,并使用fromStream方法将文件的内容转换为流。例如:

final observable = Observable.fromStream(file.openRead());

使用rxdart提供的操作符对流进行处理。例如使用listen方法来订阅流,并在每次数据可用时执行相应的操作。

observable.listen((data) {
  // 在这里处理读取到的数据
  print(data);
}, onError: (error) {
  // 处理错误
  print(error);
}, onDone: () {
  // 处理完成事件
  print('读取完成');
});

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

zsh中安装ros-<ros2-distro>-turtlebot3*失败 || 以humble为例

在zsh中尝试使用 sudo apt install ros-<ros2-distro>-turtlebot3* 安装turtlebot3相关仿真包失败&#xff0c;报错E: 无法定位软件包。 但是在bash中尝试使用同样的命令却可以安装。 原因是zsh中如果要使用通配符&#xff0c;那么一定要放在字符串里&#xff0c;以上…

冒泡排序【Java算法】

文章目录 1. 概念2. 思路3. 代码实现 1. 概念 比较前后相邻的两个数据&#xff0c;如果前面数据大于后面的数据&#xff0c;就将这两个数据互换。这样对数组的第0个数据到第 N - 1 个数据进行一次遍历后&#xff0c;最大的一个数据就 “沉” 到数组的第 N - 1 个位置。 N N - …

UI自动化测试之日志记录和单元测试

设置日志 很多公司要求自动化测试需要有日志记录。 记录程序运行时的状态和结果&#xff0c;方便排查问题。 实现步骤&#xff1a; ①在项目中创建config目录&#xff08;不用创建包&#xff09;&#xff0c;复制粘贴log.conf文件 ②在项目中创建logs目录&#xff0c;用于…

第三方材料检测实验室LIMS系统源码 lims源码

实验室LIMS系统采用国际规范的业务管理流程和严格的质量控制体系&#xff0c;对每个检测流程节点采用 “人、机、料、法、环、测”进行质量控制&#xff0c;可记录&#xff0c;可追溯。强大的数据查询和统计分析能力&#xff0c;提高工作效率&#xff1b;自动化地采集实验室原始…

轻量化YOLOv5改进 | 结合repghost结构冲参数化网络,实现轻量化和加速推理,

RepGhost: A Hardware-Efficient Ghost Module via Re-parameterization 论文总结本文改进repghost 核心代码测试参数量和计算量🔥🔥🔥 “引入RepGhostNet以加速CNN网络推理” “网络宽度的自定义调整:无缝嵌入YOLOv5” “通过结构重参数化优化网络性能” “实现高效和…

nexus迁移

数据和配置迁移 打包两个目录&#xff0c;配置nexus-2.13.0-01和数据sonatype-work 数据量大可以split分割之后迁移再合并 大概看下文件目录 [roottest nexus]# tree -L 3 . ├── nexus-2.13.0-01 │ ├── bin │ │ ├── jsw │ │ ├── nexus │ │ …

拥抱未来:2023年及以后十大DevOps工具的最终清单!

软件开发和 IT 运营的世界在不断发展&#xff0c;DevOps 已成为寻求敏捷性、效率和协作的组织的重要方法。随着我们进入 2023 年及以后&#xff0c;DevOps 格局将随着新工具的引入和现有工具的改进而不断发展。在本文中&#xff0c;我们列出了将在未来几年塑造行业的十大DevOps…

2023年08月IDE流行度最新排名

点击查看最新IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年08月IDE流行度最新排名 顶级IDE排名是通过分析在谷歌上搜索IDE下载页面的频率而创建的 一个IDE被搜索的次数越多&#xff0c;这个IDE就被认为越受欢迎。原始数据来自谷歌Trends 如果您相信集体智慧&am…

如何克服看到别人优于自己而感到的焦虑和迷茫?

文章目录 每日一句正能量前言简述自己的感受怎么做如何调整自己的心态后记 每日一句正能量 行动是至于恐惧的良药&#xff0c;而犹豫、拖延&#xff0c;将不断滋养恐惧。 前言 虽然清楚知识需要靠时间沉淀&#xff0c;但在看到自己做不出来的题别人会做&#xff0c;自己写不出的…

2023华数杯数学建模A题2023华数杯A 题隔热材料的结构优化控制研究

问题1 问题1&#xff1a; 该问题需要建立一个数学模型来描绘织物整体热导率与单根纤维热导率之间的关系。这个模型需要考虑织物的结构&#xff08;如纤维的排列、空隙大小和分布等&#xff09;以及纤维和空隙中的空气对热传导的贡献。此外&#xff0c;我们需要根据织物的整体热…

网页版Java五子棋项目(一)websocket【服务器给用户端发信息】

网页版Java五子棋项目&#xff08;一&#xff09;websocket【服务器给用户端发信息】 一、为什么要用websocket二、websocket介绍原理解析 三、代码演示1. 创建后端api&#xff08;TestAPI&#xff09;新增知识点&#xff1a;extends TextWebSocketHandler重写各种方法 2. 建立…

LeetCode--剑指Offer75(2)

目录 题目描述&#xff1a;剑指 Offer 58 - II. 左旋转字符串&#xff08;简单&#xff09;题目接口解题思路1代码解题思路2代码 PS: 题目描述&#xff1a;剑指 Offer 58 - II. 左旋转字符串&#xff08;简单&#xff09; 字符串的左旋转操作是把字符串前面的若干个字符转移到…

Win11系统下FTP服务器搭建(用于Cadence 中心库服务器搭建)(上)

本文用于快速搭建一个FTP服务器 一、配置IIS Web服务器 使用快捷键【Win R】打开运行窗口&#xff0c;并输入optionalfeatures进入配置windows功能&#xff1b; 选择红框标识选项后&#xff0c;点击确定。 二、配置IIS web 站点 1.首先我们在本地创建一个ftp服务器的根目录…

【Pycharm2022.2.1】python编辑器最新版安装教程(包含2017-2022的所有版本win/mac/linux)

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 永久安装 Pycharm&#xff08;2017-2022的win/mac/linux所有版本&#xff09;/ IntelliJ IDEA也可以, 按照本文教程所写的&#xff0c;具体步骤跟着下面的图文教程一步一步来就行&#xff0c;一分钟即可搞定&#xff0c;过…

超详情的开源知识库管理系统- mm-wiki的安装和使用

背景&#xff1a;最近公司需要一款可以记录公司内部文档信息&#xff0c;一些只是累计等&#xff0c;通过之前的经验积累&#xff0c;立马想到了 mm-wiki&#xff0c;然后就给公司搭建了一套&#xff0c;分享一下安装和使用说明&#xff1a; 当前市场上众多的优秀的文档系统百…

从零搭建一个react + electron项目

最近打算搭建一个react electron的项目&#xff0c;发现并不是那么傻瓜式 于是记录一下自己的实践步骤 通过create-react-app 创建react项目 npx create-react-app my-app 安装electron依赖 npm i electron -D暴露react项目的配置文件&#xff08;这一步看自己需求&#xff0c…

Golang之路---03 面向对象——类型断言

类型断言 作用 检查 i 是否为 nil检查 i 存储的值是否为某个类型 使用方式 第一种&#xff1a; t : i.(T)这个表达式可以断言一个接口对象&#xff08;i&#xff09;里不是 nil&#xff0c;并且接口对象&#xff08;i&#xff09;存储的值的类型是 T&#xff0c;如果断言成…

什么是文件传输协议:文件传输协议初学者介绍

FTP&#xff08;文件传输协议&#xff09;是一种在TCP / IP&#xff08;传输控制协议/ Internet协议&#xff09;网络&#xff08;也就是Internet&#xff09;上实现计算机之间交换和分享文件的方法。用户只要获得了访问权限&#xff0c;就可以在文件传输协议服务器&#xff08…

c++学习(lambda+bind)[27]

lambda 本质&#xff1a;编译器生成一个类 简单的lambda函数 int mian() {//两个数相加的lambdaauto add1 [](int a, int b)->int{return a b; };cout << add1(1, 2) << endl;//省略返回值auto add2 [](int a, int b){return a b; };cout << add2…

国标GB28181视频监控EasyGBS接入大量通道,创建角色接口未响应

国标GB28181协议视频平台EasyGBS是基于国标GB28181协议的视频云服务平台&#xff0c;支持多路设备同时接入&#xff0c;并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。平台可提供视频监控直播、云端录像、云存储、检索回放、智能告警、语音对讲、平台级…