当前位置: 首页 > article >正文

全面解析Flutter中的Stream用法及实际应用

目录

前言

1. 什么是Stream

2. Stream的分类

1.单订阅

2.广播订阅

3. Stream的基础用法

1.创建

1.使用Stream.periodic

2.使用StreamController

2.监听

1.Stream类的监听

2.StreamController

3.关闭Stream

4.Stream的常用方法

1.empty

2.value

5.实际应用场景

6.完整示例:计数器应用

7.总结

8.参考文章


前言

1. 什么是Stream

        在 Flutter 中,Stream 是一种用于处理异步数据流的工具。它的作用类似广播频道,允许连续推送数据流,并让其他部分实时监听这些数据。Stream 广泛应用于需要异步处理的场景,比如网络请求、用户输入、数据流实时更新等。

        图1.Stream

        如图所示,把我们要处理的每一个事件都看做一个流,每个流处理之后都会被放到Flutter的事件循环中去。       

2. Stream的分类

        在 Flutter 中,Stream 分为两类:

1.单订阅

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

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

        通常用于一次性的事件,比如从 API 获取数据。

2.广播订阅

        广播(Broadcast Stream)允许多个监听器,可以同时向多个订阅者推送数据。

        这种类型适合用于事件广播,比如用户操作、全局数据推送等。

3. Stream的基础用法

1.创建

        可以使用 Stream 的构造方法,也可以通过 StreamController 自定义数据流。

1.使用Stream.periodic

        我们可以使用Stream.periodic定时生成数据流:

Stream<int> numberStream = Stream.periodic(Duration(seconds: 1), (count) => count);

2.使用StreamController

        Flutter还可以使用StreamController手动推送数据。

final StreamController<int> controller = StreamController<int>();

Stream<int> myStream = controller.stream;

controller.sink.add(1); // 推送数据

2.监听

1.Stream类的监听

        当我们使用Stream类直接创建数据流的时候,可以使用StreamBuilder来监听。

        使用 listen 方法可以轻松监听 Stream 的数据变化。

       例如在下面的代码中,当我点击按钮的时候,开始每隔1秒发送数据,我们可以使用StreamBuilder 来监听 stream,这样一旦流发出数据,界面就会自动更新。

        图2.Stream流的监听

代码如如下:

import 'package:flutter/material.dart';

class StreamDemoPage extends StatefulWidget {
  const StreamDemoPage({super.key});

  @override
  State<StreamDemoPage> createState() => _StreamDemoPageState();
}

class _StreamDemoPageState extends State<StreamDemoPage> {
  late Stream<int> stream;
  late StreamController<int> streamController;

  @override
  void initState() {
    super.initState();
    // 初始化一个流
    stream = Stream<int>.periodic(const Duration(seconds: 1), (count) => count * count).take(5);
  }

  void startStream() {
    setState(() {
      // 每次点击“发送数据”按钮时重新初始化流
      stream = Stream<int>.periodic(const Duration(seconds: 1), (count) => count * count).take(5);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.purple,
        title: const Text(
          'Flutter中的Stream',
          style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16),
        ),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: startStream,
              child: const Text('发送数据', style: TextStyle(color: Colors.black, fontSize: 12)),
            ),
            StreamBuilder<int>(
              stream: stream,
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const Text('等待数据...', style: TextStyle(color: Colors.black, fontSize: 12));
                } else if (snapshot.hasError) {
                  return Text('错误: ${snapshot.error}', style: const TextStyle(color: Colors.red, fontSize: 12));
                } else if (snapshot.connectionState == ConnectionState.done) {
                  return const Text('数据发送完成', style: TextStyle(color: Colors.black, fontSize: 12));
                } else {
                  return Text('接收数据: ${snapshot.data}', style: const TextStyle(color: Colors.black, fontSize: 12));
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

2.StreamController

        当我们的数据流比较复杂的时候,可以使用StreamController 来管理流的创建和数据发送。StreamController 可以在我们需要时手动添加事件,适用于更复杂的流控制需求。

        还是以上面的代码为例,我们看看如何使用StreamController来实现:

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

class StreamDemoPage extends StatefulWidget {
  const StreamDemoPage({super.key});

  @override
  State<StreamDemoPage> createState() => _StreamDemoPageState();
}

class _StreamDemoPageState extends State<StreamDemoPage> {
  late StreamController<int> streamController;
  int counter = 0;
  Timer? timer;

  @override
  void initState() {
    super.initState();
    streamController = StreamController<int>();
  }

  void startStream() {
    // 重置计数器并关闭之前的计时器(如果存在)
    counter = 0;
    timer?.cancel();

    // 每秒添加一个计数的平方值到流中
    timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (counter < 5) {
        streamController.add(counter * counter); // 将数据添加到流中
        counter++;
      } else {
        timer.cancel(); // 停止计时器
        streamController.close(); // 关闭流
      }
    });
  }

  @override
  void dispose() {
    timer?.cancel(); // 确保计时器在页面销毁时取消
    streamController.close(); // 关闭流控制器
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.purple,
        title: const Text(
          'Flutter中的StreamController',
          style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16),
        ),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: startStream,
              child: const Text('发送数据', style: TextStyle(color: Colors.black, fontSize: 12)),
            ),
            StreamBuilder<int>(
              stream: streamController.stream,
              builder: (context, snapshot) {
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const Text('等待数据...', style: TextStyle(color: Colors.black, fontSize: 12));
                } else if (snapshot.hasError) {
                  return Text('错误: ${snapshot.error}', style: const TextStyle(color: Colors.red, fontSize: 12));
                } else if (snapshot.connectionState == ConnectionState.done) {
                  return const Text('数据发送完成', style: TextStyle(color: Colors.black, fontSize: 12));
                } else {
                  return Text('接收数据: ${snapshot.data}', style: const TextStyle(color: Colors.black, fontSize: 12));
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

3.关闭Stream

        使用 StreamController 时,在不再需要时调用 close 方法来释放资源,防止内存泄漏。

controller.close();

4.Stream的常用方法

        Stream 提供了许多用于数据流转换和处理的方法,比如 map、where、asyncMap 等。可以使用这些方法高效地处理流数据。

1.empty

        这个方法用来创建一个空的数据流,同时数据流的状态为为完成。

        例如下面的代码中,我们创建一个空的Stream:

const stream = Stream.empty();
stream.listen((message){
  debugPrint('stream: $variables');
},onDone: (){
  debugPrint('stream done');
},
  onError: (error){
  debugPrint('error:$error');
  }
);

        控制台打印信息为stream done!。

2.value

        方法用于返回在数据流关闭之前的数据。

        例如在下面的代码中,我们调用printThings方法返回的信息为ok。

Future<void> printThings(Stream<String> data)async{
  await for (var x in data){
    debugPrint(x);
  }
}
printThings(Stream<String>.value('ok'));

        

5.实际应用场景

        Flutter中Stream的应用场景如下:

  1. 网络请求:使用 Stream 可以轻松处理 HTTP 请求响应,尤其适合需要连续多次请求的场景。
  2. 用户输入监听:监听用户输入变化,比如搜索框的输入变化。
  3. 实时数据更新:比如股票、天气、聊天应用等需要实时数据推送的场景。
  4. 多事件处理:多个监听器同时监听并响应同一个事件源,比如按钮点击。

6.完整示例:计数器应用

        下面是一个简单的计数器应用示例,展示了 Stream 在应用中如何工作:

import 'dart:async';

import 'package:flutter/material.dart';

class StreamMainPage extends StatefulWidget {
  const StreamMainPage({
    super.key,
  });
  @override
  State<StreamMainPage> createState() => _StreamMainPageState();
}

class _StreamMainPageState extends State<StreamMainPage> {
  final StreamController<int> _controller = StreamController<int>();

  int _counter = 0;
  @override
  void initState() {
    super.initState();
    // 使用 Timer.periodic 定时自增计数器
    Timer.periodic(const Duration(seconds: 1), (timer) {
      _counter++;
      _controller.sink.add(_counter);
    });
  }

  @override
  void dispose() {
    _controller.close(); // 关闭 Stream 防止内存泄漏
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.purple,
        title: const Text('Stream用法',style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 16),),
      ),
      body: Center(
        child: StreamBuilder<int>(
          stream: _controller.stream,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return Text('Counter: ${snapshot.data}',
                  style: const TextStyle(fontSize: 24));
            } else {
              return const CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}

7.总结

        在 Flutter 中,Stream 是一种处理异步事件流的利器,适用于需要实时推送和监听数据的场景。通过 Stream 可以实现高效的数据流处理和转换,尤其适合需要异步响应的 UI 场景。在实际应用中,合理使用 Stream 可以使代码更加简洁和高效,也能够极大地改善用户体验。

8.参考文章

1.Flutter in Focus


http://www.kler.cn/a/384404.html

相关文章:

  • CentOS 7 更换软件仓库
  • 基于Opencv的图像处理软件
  • mysql5安装
  • SQL Server 日志记录
  • Pandas 数据分析工具详细教程
  • 如何基于pdf2image实现pdf批量转换为图片
  • ssm070基于SSM框架的校园代购服务订单管理系统的设计与实现+vue(论文+源码)_kaic
  • 开源数据库 - mysql - innodb源码阅读 - 线程启动
  • 【Hive sql 面试题】现有用户登录记录表,请查询出用户连续三天登录的所有数据记录(难)
  • css background-image背景图片轮播
  • linux常用指令整理
  • ReactPress:重塑内容管理的未来
  • 【论文阅读】火星语义分割的半监督学习
  • 【一站式学会Kotlin】第二十六 Kotlin枚举类和封闭类
  • AI Prompt如何帮你提升论文中的逻辑推理部分?
  • SpringBoot框架下的资产管理智能化
  • 前端将后端返回的文件下载到本地
  • EDM邮件营销策略:提升转化率的关键步骤!
  • 深度学习:transformer编码器详解
  • 算法学习--回溯算法
  • 如何为 Redis 设置密码
  • 数据结构---二叉树(顺序结构),堆(上)
  • 大数据-212 数据挖掘 机器学习理论 - 无监督学习算法 KMeans 基本原理 簇内误差平方和
  • Qt多边形填充/不填充绘制
  • 【jvm】Minor GC
  • 《安富莱嵌入式周报》第345期:开源蓝牙游戏手柄,USB3.0 HUB带电压电流测量,LCR电桥前端模拟,开源微型赛车,RF信号扫描仪,开源无线电收发器