flutter:Future、Stream、RxDart

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('读取完成');
});

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++画出分割图像,水平线和垂直线

1、pca 找到图像某个区域的垂直线&#xff0c;并画出来 // 1、 斑块的框 血管二值化图&#xff0c;pca 找到垂直血管壁的直线, 还是根据斑块找主轴方向吧// Step 1: 提取斑块左右范围内的血管像素点坐标&#xff0c;std::vector<cv::Point> points;for (int y 0; y <…

用Apache Echarts展示数据

目录 1.后端代码 1.1 实体类&#xff1a; 1.2 SQL语句&#xff1a; 2.前端代码 2.1 安装 Apach Echarts安装包&#xff1a; 2.2 查找数据并赋值给Echarts 思路&#xff1a;后端查到数据&#xff0c;包装为map&#xff0c;map里有日期和每日就诊人数&#xff0c;返回给前端…

异或运算详解

异或运算详解 定义特性用途总结 定义 参与运算的两个数据,按二进制位进行 ^ 运算,如果两个相对应为值相同结果为0,否则为1 1 ^ 0 1 0 ^ 1 1 0 ^ 0 0 1 ^ 1 0特性 异或^运算只能用于数值(整数) x ^ 0 x x ^ x 0用途 两个值交换,而不用使用临时变量 a a ^ b; b b ^…

css在线代码生成器

这里收集了许多有意思的css效果在线代码生成器适合每一位前端开发者 布局&#xff0c;效果类&#xff1a; 网格生成器https://cssgrid-generator.netlify.app/ CSS Grid Generator可帮助开发人员使用CSS Grid创建复杂的网格布局。网格布局是创建Web页面的灵活和响应式设计的强…

【Linux】在服务器上创建Crontab(定时任务),自动执行shell脚本

业务场景&#xff1a;该文即为上次编写shell脚本的姊妹篇,在上文基础上,将可执行的脚本通过linux的定时任务自动执行,节省人力物力,话不多说,开始操作! 一、打开我们的服务器连接工具 连上服务器后,在任意位置都可以执行:crontab -e 如果没有进入编辑cron任务模式 根据提示查看…

Day01-作业(HTMLCSS)

作业1&#xff1a;通过HTML的标签及CSS样式&#xff0c;完成如下企业简介html页面的制作 A. 最终效果如下&#xff1a; B. 文字素材如下&#xff1a; 企业简介传智教育(股票代码 003032)&#xff0c;隶属江苏传智播客教育科技股份有限公司&#xff0c;注册资本4亿元&#xff0…

国产GOWIN实现低成本实现CSI MIPI转换DVP

CSI MIPI转换DVP&#xff0c;要么就是通用IC操作&#xff0c;如龙讯芯片和索尼芯片&#xff0c;但是复杂的寄存器控制器实在开发太累。对于FPGA操作&#xff0c;大部分都是用xilinx的方案&#xff0c;xilinx方案成本太高&#xff0c;IP复杂。 而用国产GOWIN已经实现了直接mipi …

腾讯云TencentOS Server镜像系统常见问题解答

腾讯云TencentOS Server镜像是腾讯云推出的Linux操作系统&#xff0c;完全兼容CentOS生态和操作方式&#xff0c;TencentOS Server操作系统为云上运行的应用程序提供稳定、安全和高性能的执行环境&#xff0c;TencentOS可以运行在腾讯云CVM全规格实例上&#xff0c;包括黑石物理…

jmeter之接口测试(http接口测试)

基础知识储备 一、了解jmeter接口测试请求接口的原理 客户端--发送一个请求动作--服务器响应--返回客户端 客户端--发送一个请求动作--jmeter代理服务器---服务器--jmeter代理服务器--服务器 二、了解基础接口知识&#xff1a; 1、什么是接口&#xff1a;前端与后台之间的…

【设计模式】单例模式

什么是单例模式&#xff1f; 保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点 单例模式的应用场景 1.整个程序的运行中只允许有一个类的实例&#xff1b; 2.需要频繁实例化然后销毁的对象。 3.创建对象时耗时过多或者耗资源过多&#xff0c;但又经常用到…

Python web实战 | 使用 Flask 实现 Web Socket 聊天室

概要 今天我们学习如何使用 Python 实现 Web Socket&#xff0c;并实现一个实时聊天室的功能。本文的技术栈包括 Python、Flask、Socket.IO 和 HTML/CSS/JavaScript。 什么是 Web Socket&#xff1f; Web Socket 是一种在单个 TCP 连接上进行全双工通信的协议。它是 HTML5 中的…

Flutter Flar动画实战

在Flare动面出现之前,Flare动画大体可以分为使用AnimationController控制的基础动画以及使用Hero的转场动画,如果遇到一些复杂的场景,使用这些动画方案实现起来还是有难度的。不过,随着Flutter开始支持Flare矢量动面,Flutter的动画开发也变得越来越简单。事实上,Flare动画…

2024考研408-计算机网络 第三章-数据链路层学习笔记

文章目录 前言一、数据链路层的功能1.1、数据链路层的研究思想1.2、数据链路层基本概念1.3、数据链路层功能概述&#xff08;5个功能&#xff09; 二 、组帧2.1、封装成帧以及发送帧的过程&#xff08;包含名词解释&#xff09;2.2、实现透明传输及四种组帧方法2.2.1、什么是透…

为什么马斯克和奥特曼都想重振加密货币?

1、前言 加密货币已经死了吗&#xff1f;这个问题的答案取决于谁来回答。一个加密爱好者会给你一百个不同的理由来解释为什么加密货币没有死。特斯拉CEO埃隆马斯克和OpenAI CEO 山姆奥特曼都对加密货币及其在塑造未来世界中的潜在作用有着浓厚的兴趣。 在过去很长一段时间里&…

涛思数据与拾贝云达成战略合作,携手赋能工业数字化转型

2023 年 7 月 27 日&#xff0c;北京涛思数据科技有限公司&#xff08;以下简称“涛思数据”&#xff09;与广州拾贝云科技有限公司&#xff08;以下简称“拾贝云”&#xff09;于广州签署战略合作协议。双方围绕电力行业的需求与痛点展开积极讨论&#xff0c;就如何量身打造最…

ZKML——EZKL团队分享

1. 引言 “ZKP之于数字签名” 类似于 “以太坊之于比特币”&#xff1a; 所谓数字签名&#xff0c;是指&#xff1a;“我知道某秘密secrets&#xff0c;使得 F(secrets, public inputs)pubic outputs”&#xff0c;其中F为fixed function&#xff08;固定函数&#xff09;。这…

ssl单向证书和双向证书校验测试及搭建流程

零、前提准备 首先了解下HTTP和HTTPS的区别&#xff1a; HTTPS与HTTP有什么不同&#xff1f; HTTP是过去很长一段时间我们经常用到的一种传输协议。HTTP协议传输的数据都是未加密的&#xff0c;这就意味着用户填写的密码、账号、交易记录等机密信息都是明文&#xff0c;随时…

SpringBoot项目修改中静态资源,只需刷新页面无需重启项目(附赠—热加载)

初衷 &#x1f4a2;初衷&#x1f4a2; 因为一遍遍修改并重启项目觉得很麻烦&#xff0c;所以刚开始就自己给项目配置了热加载&#xff0c;但奈何代码更新还是慢&#xff0c;还不如我重启一遍项目的速度&#xff0c;所以放弃了自己上网找到的热加载配置。直到我debugger前端代码…

ELFK——ELK结合filebeat日志分析系统(2)

目录 一、filebeat 二、ELFK 1.原理简介 2.在ELK基础上部署filebeat 一、filebeat Filebeat&#xff0c;轻量级的开源日志文件数据搜集器。通常在需要采集数据的客户端安装 Filebeat&#xff0c;并指定目录与日志格式&#xff0c;Filebeat 就能快速收集数据&#xff0c;并…

个人中心 - 实现修改用户头像、用户名或密码

目录 1. 修改用户头像 1.1 获取原来的用户头像和用户名 1.2 实现保存头像 2. 修改用户名或密码 1. 修改用户头像 本文是针对之前的一篇项目博客 - 博客系统 做的一个扩展功能. 1.1 获取原来的用户头像和用户名 想要修改头像, 那么就得先获取数据库中原来的头像, 此处顺便…