Flutter 09 Future 和 Stream

news2024/9/20 8:42:48

一、Future 和 Stream 是处理异步操作的两个重要概念:

Future:

Future 用于表示一个延迟操作的值或错误,即异步操作的结果。通过 Future,可以在异步操作完成后获取其结果。可以使用 async 和 await 关键字来处理 Future,使得异步操作更加直观和易于管理。

Stream:

Stream 则用于处理一系列异步数据(事件)的序列。它可以持续地产生数据,而不是单一的结果。Stream 可以用于处理事件、文件读写、状态管理等需求。使用 StreamController 可以很好的控制 Stream 的创建和数据添加。

二、Future的使用:

async 和 await。

async:用于在方法或函数声明前添加,表示该方法是一个异步方法。在异步方法中,执行顺序可以是非阻塞的,不会阻塞当前线程。

await:用于在异步方法中等待并获取异步表达式的执行结果,只能在被 async 修饰的方法中使用。

1、模拟一个异步请求数据的实现

通过async关键字声明一个异步方法,延迟2秒后返回模拟的结果String值。

Future<String> getAsyncData() async {
    setState(() {
      asyncData = "开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    return "返回:Future get async data.";
}

通过按钮点击触发模拟接口请求,获取到异步数据后更新到页面中。 

ElevatedButton(
  onPressed: () {
	getAsyncData().then((value) {
	  setState(() {
		asyncData = value;
	  });
	});
  },
  child: const Text("Future get async data.")),
Text(asyncData),

2、模拟两个异步请求并行,待全部接口请求完成后获取数据的实现

通过async关键字声明两个异步方法,分别延迟2秒和延迟3秒后,返回模拟的结果String值。

Future<String> getAsyncData2() async {
    setState(() {
      asyncTowData = "getAsyncData2开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      asyncTowData = "getAsyncData2完成请求数据";
    });
    return "延迟2秒返回";
}

Future<int> getAsyncData3() async {
    setState(() {
      asyncTowData = "getAsyncData3开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 3));
    setState(() {
      asyncTowData = "getAsyncData3完成请求数据";
    });
    return 3;
}

创建第三个异步方法,将上面的两个异步方法添加到数组里面,并行执行。

// 同时执行两个异步方法并等待它们的结果
Future<List<dynamic>> getTowAsyncData() async {
    List<Future<dynamic>> futures = [getAsyncData2(), getAsyncData3()];
    List<dynamic> result = await Future.wait(futures);
    return result;
}

通过按钮点击触发模拟接口请求,获取到异步数据后更新到页面中。 

ElevatedButton(
  onPressed: () {
	getTowAsyncData().then((value) {
	  var data1 = value[0] as String;
	  var data2 = value[1] as int;
	  setState(() {
		asyncTowData = "$data1,延迟$data2秒返回";
	  });
	});
  },
  child: const Text("Future get tow async data.")),
Text(asyncTowData),

3、FutureBuilder 使用

通过FutureBuilder实现请求数据时展示加载组件,请求成功后显示数据。

Widget _init() {
    return FutureBuilder(
      future: initAsyncData(),
      builder: (context, snapshot) {
        // 等待状态显示的widget
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Center(
            child: CircularProgressIndicator(),
          );
          //  错误时显示的widget
        } else if (snapshot.hasError) {
          return const Text('Error');
          // 加载完成后显示的数据
        } else {
          return _widget();
        }
      },
    );
}

4、FuturePage完整代码

import 'package:flutter/material.dart';

///create by itz on 2024/8/16 14:55
///desc : 
class FuturePage extends StatefulWidget {
  const FuturePage({super.key});

  @override
  State<FuturePage> createState() => _FuturePageState();
}

class _FuturePageState extends State<FuturePage> {

  String asyncData = "";
  String asyncTowData = "";
  bool isInit = false;

  Future initAsyncData() async {
    await Future.delayed(const Duration(seconds: 2));
    isInit = true;
  }

  Future<String> getAsyncData() async {
    setState(() {
      asyncData = "开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    return "返回:Future get async data.";
  }

  Future<String> getAsyncData2() async {
    setState(() {
      asyncTowData = "getAsyncData2开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      asyncTowData = "getAsyncData2完成请求数据";
    });
    return "延迟2秒返回";
  }

  Future<int> getAsyncData3() async {
    setState(() {
      asyncTowData = "getAsyncData3开始请求数据";
    });
    await Future.delayed(const Duration(seconds: 3));
    setState(() {
      asyncTowData = "getAsyncData3完成请求数据";
    });
    return 3;
  }

  // 同时执行两个异步方法并等待它们的结果
  Future<List<dynamic>> getTowAsyncData() async {
    List<Future<dynamic>> futures = [getAsyncData2(), getAsyncData3()];
    List<dynamic> result = await Future.wait(futures);
    return result;
  }

  @override
  Widget build(BuildContext context) {
    Widget w = isInit ? _widget() : _init();
    return Scaffold(
      appBar: AppBar(
        title: const Text("Future"),
      ),
      body: w,
    );
  }

  Widget _init() {
    return FutureBuilder(
      future: initAsyncData(),
      builder: (context, snapshot) {
        // 等待状态显示的widget
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Center(
            child: CircularProgressIndicator(),
          );
          //  错误时显示的widget
        } else if (snapshot.hasError) {
          return const Text('Error');
          // 加载完成后显示的数据
        } else {
          return _widget();
        }
      },
    );
  }

  Widget _widget() {
    return Container(
      margin: const EdgeInsets.all(16),
      child: ListView(
        children: [
          ElevatedButton(
              onPressed: () {
                getAsyncData().then((value) {
                  setState(() {
                    asyncData = value;
                  });
                });
              },
              child: const Text("Future get async data.")),
          Text(asyncData),
          const SizedBox(height: 10),
          ElevatedButton(
              onPressed: () {
                getTowAsyncData().then((value) {
                  var data1 = value[0] as String;
                  var data2 = value[1] as int;
                  setState(() {
                    asyncTowData = "$data1,延迟$data2秒返回";
                  });
                });
              },
              child: const Text("Future get tow async data.")),
          Text(asyncTowData),
        ],
      ),
    );
  }
}

三、Stream的使用

在Flutter中,Stream 是用于处理异步事件序列的概念,常见应用包括:

异步数据获取:Stream在异步数据获取方面非常有用。例如,在网络请求中,可以使用 Stream 来处理异步数据的传输和响应。通过监听 Stream,可以实时获取数据并更新应用程序的界面,实现动态数据展示的功能。

状态管理:Stream在状态管理中扮演着重要的角色。通过创建包含状态信息的 Stream,可以在应用程序中管理状态的变化。当状态发生变化时,通过 Stream 向订阅者(监听者)发送新的状态信息,从而触发相应的操作或界面更新。

事件总线:Stream可以作为事件总线,用于在应用程序中处理和传递事件。通过创建一个全局的 Stream,不同部分的应用程序可以监听并发送事件,实现模块之间的通信和交互。这种方式可以实现解耦和灵活的组件通信。

文件读写:Stream`也可以用于处理文件读写操作。例如,读取大文件时可以使用 Stream实现分块读取,提高读取效率。同样,可以通过 Stream 监听文件写入事件,实现实时监控文件变化等功能。

1、异步数据获取

1)创建StreamController 控制器,控制 Stream 的创建、数据添加 和 关闭等操作。

final StreamController _streamController = StreamController();

2)创建异步方法,模拟异步数据获取

getStreamData() async {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(const Duration(seconds: 1));
      // 发送值
      _streamController.sink.add(i);
    }
    await Future.delayed(const Duration(seconds: 1));
    // 发送值
    _streamController.sink.add("done");
}

3)创建StreamBuilder用于接收流式数据

StreamBuilder(
  stream: _streamController.stream,
  builder: (context, snapshot) {
	if (snapshot.hasData) {
	  return Text('异步数据:${snapshot.data}');
	} else if (snapshot.hasError) {
	  return Text('发生错误:${snapshot.error}');
	} else {
	  return const Text('加载中...');
	}
  })

4)通过按钮点击触发模拟接口请求

ElevatedButton(
  onPressed: () {
	getStreamData();
  },
  child: const Text("stream get async data.")),

5)完整代码 

/*年轻人,只管向前看,不要管自暴自弃者的话*/

import 'dart:async';

import 'package:flutter/material.dart';

///create by itz on 2024/8/16 15:46
///desc :
class StreamPage extends StatefulWidget {
  const StreamPage({super.key});

  @override
  State<StreamPage> createState() => _StreamPageState();
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Stream"),
      ),
      body: _widget(),
    );
  }

  Widget _widget() {
    return Container(
      margin: const EdgeInsets.all(16),
      child: ListView(
        children: [
          ElevatedButton(
              onPressed: () {
                getStreamData();
              },
              child: const Text("stream get async data.")),
          StreamBuilder(
              stream: _streamController.stream,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Text('异步数据:${snapshot.data}');
                } else if (snapshot.hasError) {
                  return Text('发生错误:${snapshot.error}');
                } else {
                  return const Text('加载中...');
                }
              })
        ],
      ),
    );
  }

  getStreamData() async {
    for (int i = 0; i < 10; i++) {
      await Future.delayed(const Duration(seconds: 1));
      // 发送值
      _streamController.sink.add(i);
    }
    await Future.delayed(const Duration(seconds: 1));
    // 发送值
    _streamController.sink.add("done");
  }
}

2、事件总线

1)创建EventBus单例类

import 'dart:async';

///create by itz on 2024/8/16 15:54
///desc :
class EventBus {
  static final EventBus _instance = EventBus._internal();

  factory EventBus() => _instance;

  EventBus._internal();

}

2)使用 broadcast() 方法创建了一个可以实时广播事件的 StreamController

final _controller = StreamController<dynamic>.broadcast();

3)创建 get stream,发送事件,关闭控制器等方法

  Stream get stream => _controller.stream;

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

  void dispose() {
    _controller.close();
  }

4)使用EventBus,设置EventBus事件监听

    EventBus().stream.listen((event) {
      setState(() {
        eventInfo = event;
      });
    });

5)发送EventBus事件

sendEvent(String event) {
    EventBus().fire(event);
  }

6)通过按钮点击触发发送事件

ElevatedButton(
	onPressed: () {
	  sendEvent("cat");
	},
	child: const Text("send Event.")),
Text(eventInfo),

7)完整EventBus代码

import 'dart:async';

///create by itz on 2024/8/16 15:54
///desc :
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();
  }
}

8)完整EventBusPage代码

import 'package:async_test/util/EventBus.dart';
import 'package:flutter/material.dart';

///create by itz on 2024/8/16 16:01
///desc :
class EventBusPage extends StatefulWidget {
  const EventBusPage({super.key});

  @override
  State<EventBusPage> createState() => _EventBusPageState();
}

class _EventBusPageState extends State<EventBusPage> {
  String eventInfo = "";

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    EventBus().stream.listen((event) {
      setState(() {
        eventInfo = event;
      });
    });
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    EventBus().dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("EventBus"),
      ),
      body: Container(
        margin: const EdgeInsets.all(16),
        child: ListView(
          children: [
            ElevatedButton(
                onPressed: () {
                  sendEvent("cat");
                },
                child: const Text("send Event.")),
            Text(eventInfo),
          ],
        ),
      ),
    );
  }

  sendEvent(String event) {
    EventBus().fire(event);
  }
}

Stream 适用于处理持续产生数据的异步操作,而 async/await 适用于一次性获取结果的异步操作。两者使用区别包括控制流、数据处理、使用场景和代码结构。

Provider 与 Stream 区别在于使用 Provider 时状态可以被存储起来,而 Stream 不会存储。选择使用哪种取决于需求和代码结构的复杂度。

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

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

相关文章

Cisco Catalyst 8000v Edge Software, IOS XE Release IOSXE-17.14.01a ED

Cisco Catalyst 8000v Edge Software, IOS XE Release IOSXE-17.14.01a ED Cisco Catalyst 8000V 边缘软件 - 虚拟路由器 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-catalyst-8000v/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&…

Redis笔记-分布式存储方案中哨兵模式配置

以前在系统分析师中学习到了Redis哨兵模式&#xff0c;只知道其中基本概念&#xff0c;但不知道怎么去配这个&#xff0c;今天看到某项目&#xff0c;特意记录下其配置过程。哨兵模式比主从模式&#xff0c;更具有容错性。 Redis分布式存储方案 分布式存储方案 核心特点 主从…

centos 7.9 迁移到 openEuler22.03-LTS-SP3

openEuler移植案例 | 移植操作指南 | openEuler社区官网 cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 需要两台机器&#xff0c; 不通过原因 在待升级节点检查是否有安装x2openEuler-core时, 发现已经安装了,不能作为升级节点。该节点为&#xff1a; 解…

开发日记:Object-c 中的 NSString 和 NSMutableString的常用方法

今天讲不可变数组 NSArray 可变数组 NSMutableArray。话不多说&#xff0c;直接上干货。 oc中的数组 可变数组 NSMutableArray使用初始化方法创建可变数组使用便利构造器创建可变数组可变数组追加元素可变数组追加一个不可变数组可变数组删除一个元素可变数组删除指定下标的元素…

IPD推行成功的核心要素(十七)矩阵型组织架构设计实现多组织协同以便快速抓取市场机会

随着企业规模不断增长&#xff0c;业务越来越复杂&#xff0c;组织架构对企业高效运作的影响也越来越大。加之企业所处的市场环境瞬息万变&#xff0c;机会稍纵即逝。传统金字塔型的组织架构&#xff0c;完全无法满足这样快速灵活的要求。而IPD体系设计了产品线&#xff08;IPM…

decoy靶机复现

靶机设置 设置网卡ens33 设置靶机为NAT模式 靶机IP发现 nmap 192.168.112.0/24 靶机ip为192.168.112.149 端口扫描 nmap 192.168.112.149 -p- -sV -O -Pn 访问浏览器 默认是8080&#xff0c;需要更改为80端口 下载好之后发现解压需要密码 破解密码 将该安装包放到kali中…

配置Prettier+Vscode setting提高前端开发效率

前言 大家好&#xff0c;上一篇一文读懂 系列的文章中我们介绍了前端的代码格式化校验工具ESLient。代码格式是进行自动校验了&#xff0c;但你还要一个个的微调&#xff0c;很麻烦不是吗&#xff1f; 本文介绍和ESLient配合使用的Prettier实现编译器自动将代码格式化。 同时也…

Nat Methods编委:积极探索AI与生物学交叉的高质量基于人工智能方法的论文发表|顶刊速递·24-08-19

小罗碎碎念 今天分享的这篇文献&#xff08;特刊&#xff09;发表于《Nat Methods》&#xff0c;目前IF36.1。 分享这篇文献是临时做的决定&#xff0c;因为在朋友圈看到有关这篇特刊的介绍&#xff0c;对标题很感兴趣&#xff0c;所以我立刻找到对应的文章来详细分析一下。 在…

Leetcode JAVA刷刷站(35)搜索插入位置

一、题目概述 二、思路方向 为了实现这一功能&#xff0c;我们可以使用二分查找的变种来找到目标值或确定其应插入的位置。在二分查找过程中&#xff0c;我们不仅检查中间元素是否等于目标值&#xff0c;还根据比较结果更新搜索的上下界。如果目标值大于中间元素&#xff0c;则…

一文带你从源码中学习那些实用的位运算技巧

写在文章开头 众所周知位运算执行效率高于常规运算,通过不同的位运算搭配可以让我们写出简洁高效的代码表达式,所以本文从各大开源项目的源码中介绍一下各类运算技巧及其使用场景。 Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 ,也是…

【体检】程序人生之健康检查,全身体检与预防疫苗,五大传染病普筛,基因检测等

程序员养生指南之 【体检】程序人生之健康检查&#xff0c;全身体检项目分类&#xff0c;五大传染病普筛&#xff0c;基因检测等 文章目录 一、全身体检与预防疫苗&#xff08;年检&#xff09;1、实验室检测&#xff1a;生化全套检查2、医技检查&#xff1a;辅助诊疗科室3、科…

【Python实现禁用任务管理器】

效果展示&#xff1a; 代码及解析&#xff1a; 提示&#xff1a;使用pyinstaller打包可以避免弹窗&#xff0c;食用更佳哦 提示(2)&#xff1a;运行后要等待一段时间再关机哦 禁用代码&#xff1a; 要禁止任务管理器关闭 Python 程序&#xff0c;可以通过修改注册表来实现。以…

【OpenCV_python】凸包检测 轮廓特征 直方图均衡化 模板匹配 霍夫变换

凸包特征检测 凸包就是图像的最小外接多边形&#xff0c;通过图像的轮廓点&#xff0c;找到距离最远的两个点的直线&#xff0c;根据直线找到距离最远的下一个点&#xff0c;直到所有的点被包围在多边形内 读取图像二值化找图像的轮廓获取凸包点的坐标绘制凸包点 convexHull 获…

bilibiliDown-纯净B站视频解析提取工具

软件简介 bilibiliDown是一款简洁好用的B站视频下载工具&#xff0c;支持由UP主上传的单集&#xff0c;多集以及相关封面&#xff0c;弹幕&#xff0c;字幕&#xff0c;音乐&#xff0c;刮削等等&#xff0c;支持任意粒度批量组合&#xff0c;登录后支持到1080P&#xff0c;大…

Git Merge 实例教学:同步代码库与处理分支冲突的最佳实践

文章目录 前言一、git merge是什么&#xff1f;二、git merge基本用法1. 合并两个分支2. 合并时创建合并提交3. 快进合并&#xff08;Fast-forward Merge&#xff09;4. --abort参数5. --continue参数6. -X theirs参数 三、实际例子1. 更新仓库2. 将origin/main分支合并到当前分…

podman安装过程记录

最近注意到podman兼容docker镜像包&#xff0c;于是好奇心的驱动下&#xff0c;研究了一下 本来以为podman的desktop跟docker一样&#xff0c;最后发现podman桌面版是轻量级的&#xff0c;还需要安装podman的服务&#xff08;当然&#xff0c;是支持直接通过它的桌面版来安装其…

Linux系统中用户、用户组及文件权限的常用知识汇总

Linux为多用户多任务的操作系统&#xff0c;文件权限管理十分重要&#xff0c;每一个文件都有很多属性&#xff0c;合理的权限设置&#xff0c;可以确保数据不被未授权的人员访问&#xff0c;同时也能提高系统安全性。 本文将介绍Linux系统中用户、用户组及文件权限的常用知识…

【C++二分查找 前缀和 】1292. 元素和小于等于阈值的正方形的最大边长

本文涉及的基础知识点 C二分查找 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode1292. 元素和小于等于阈值的正方形的最大边长 给你一个大小为 m x n 的矩阵 mat 和一个整数阈值 threshold。 请你返回元素总和小于或等于阈值的正…

C++ 设计模式——策略模式

策略模式 策略模式主要组成部分例一&#xff1a;逐步重构并引入策略模式第一步&#xff1a;初始实现第二步&#xff1a;提取共性并实现策略接口第三步&#xff1a;实现具体策略类第四步&#xff1a;实现上下文类策略模式 UML 图策略模式的 UML 图解析 例二&#xff1a;逐步重构…

主成分分析SPSS步骤+Matlab程序

SPSS 导入数据 主成分分析 参数设置 选择要压缩的变量 输出结果 越陡说明信息差越大&#xff0c;反之信息差越小 导出数据 双击可以复制 粘贴到matlab 计算 Matlab clc,clear data readmatrix(例2.xlsx); %将数据保存在txt文件中 data zscore(data); %数据的标准化 …