一、前言:为什么必须吃透 Future & Stream?

Dart 是单线程事件循环模型,所有耗时操作(网络请求、本地IO、蓝牙通信、定时任务)都不能阻塞 UI 主线程,异步编程是 Flutter 开发的核心基石。

绝大多数 Flutter 开发者的异步困惑,根源是分不清两个核心概念:

  • Future:单次异步任务,一次执行、一次结果、即刻结束

  • Stream:持续异步数据流,持续监听、多次推送、可无限流转

简单通俗类比:

  • Future:点外卖,下单后只送达一次,任务结束

  • Stream:实时直播,持续不断推送画面,可随时观看、随时关闭

本文从零拆解两者底层原理、语法用法、进阶技巧、场景差异,搭配超多极简可运行代码示例,结合 Flutter 专属的 FutureBuilder、StreamBuilder 实战,彻底搞定 Flutter 异步开发。

二、Future 全方位详解(单次异步)

1. Future 核心特性

  • 生命周期:等待中(Pending)→ 成功(Resolved)/ 失败(Rejected)

  • 仅返回 一个结果,任务完成即销毁

  • 用于一次性耗时操作:网络请求、文件读取、单次数据库查询

2. Future 基础用法示例(3种写法)

写法1:原始 then / catchError 链式调用
// 模拟网络请求耗时任务
Future<String> requestData() {
  return Future.delayed(const Duration(seconds: 2), () {
    // 模拟请求成功
    return "接口数据请求成功:用户信息";
    // 模拟请求异常
    // throw Exception("网络请求失败");
  });
}

void main() {
  print("开始请求数据");
  requestData().then((res) {
    print("结果:$res");
  }).catchError((err) {
    print("异常:$err");
  }).whenComplete(() {
    print("任务结束,无论成功失败都会执行");
  });
}
写法2:async / await(推荐,代码同步化)

async/await 是 Future 的语法糖,彻底告别地狱回调,代码可读性极强。

Future<void> asyncRequest() async {
  print("开始异步请求");
  try {
    String res = await requestData();
    print("结果:$res");
  } catch (e) {
    print("异常捕获:$e");
  } finally {
    print("任务最终完成");
  }
}

// 调用
// asyncRequest();
写法3:Future 立即执行/延迟执行
// 立即执行并返回成功结果
Future.value("直接成功数据");

// 立即抛出异常
Future.error(Exception("主动抛出异常"));

// 延迟执行
Future.delayed(const Duration(seconds: 1), () => print("1秒后执行"));

3. Future 进阶实战:串行/并行执行

场景1:串行执行(A执行完再执行B)

适用于依赖型任务:先获取 token,再请求接口

// 任务1:获取Token
Future<String> getToken() async {
  await Future.delayed(const Duration(seconds: 1));
  return "TOKEN_123456";
}

// 任务2:携带Token请求数据
Future<String> getUserInfo(String token) async {
  await Future.delayed(const Duration(seconds: 1));
  return "用户数据(Token:$token)";
}

// 串行调用
Future<void> serialTask() async {
  String token = await getToken();
  String userInfo = await getUserInfo(token);
  print(userInfo);
}
场景2:并行执行(A、B同时执行,节省时间)

使用 Future.wait,适用于无依赖的多个异步任务,大幅提升效率

// 并行执行多个耗时任务
Future<void> parallelTask() async {
  // 同时开启两个任务
  List<String> results = await Future.wait([
    getToken(),
    requestData(),
  ]);
  print("任务1结果:${results[0]}");
  print("任务2结果:${results[1]}");
}

串行总耗时≈2s,并行总耗时≈1s,性能提升一倍。

4. Flutter 页面实战:FutureBuilder

专门适配 Future 异步 UI,无需手动 setState,自动监听 Future 状态刷新界面。

import 'package:flutter/material.dart';

class FutureBuilderDemo extends StatelessWidget {
  const FutureBuilderDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<String>(
        future: requestData(),
        builder: (context, snapshot) {
          // 加载中
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          // 加载失败
          if (snapshot.hasError) {
            return Center(child: Text("请求失败:${snapshot.error}"));
          }
          // 加载成功
          if (snapshot.hasData) {
            return Center(child: Text("数据:${snapshot.data}"));
          }
          return const SizedBox();
        },
      ),
    );
  }
}

三、Stream 全方位详解(持续数据流)

1. Stream 核心特性

  • 持续产生数据:零次、一次、多次数据推送

  • 可监听、可暂停、可取消、可关闭

  • 用于持续性数据流场景:蓝牙广播、实时日志、下载进度、定时器、WebSocket 消息

核心关键词:异步流、持续推送、响应式

2. Stream 基础创建方式(4种常用示例)

方式1:async* + yield 生成流(最常用)

async* 标识流式异步,yield 持续推送数据(区别于 Future 的 return)

// 每秒推送一个数字,共5次
Stream<int> countDownStream() async* {
  for (int i = 5; i > 0; i--) {
    await Future.delayed(const Duration(seconds: 1));
    yield i; // 持续推送数据
  }
}

// 监听流数据
void listenStream() {
  countDownStream().listen((value) {
    print("当前数值:$value");
  }, onError: (err) {
    print("流异常:$err");
  }, onDone: () {
    print("数据流传输完成");
  });
}
方式2:StreamController 手动管控流(业务高频)

手动 add 数据、关闭流,灵活适配蓝牙、进度条等动态场景

import 'dart:async';

class StreamDemo {
  // 初始化流控制器
  final StreamController<double> _progressCtrl = StreamController();

  // 暴露流
  Stream<double> get progressStream => _progressCtrl.stream;

  // 模拟下载进度推送
  void startDownload() {
    double progress = 0;
    Timer.periodic(const Duration(milliseconds: 200), (timer) {
      progress += 0.1;
      if (progress >= 1) {
        timer.cancel();
        _progressCtrl.close(); // 关闭流
      }
      _progressCtrl.add(progress); // 推送进度数据
    });
  }
}
方式3:Stream 内置快速构造器
// 1. 从集合生成流
Stream.fromIterable([1, 2, 3, 4, 5]).listen(print);

// 2. 定时生成流(每秒推送,只取前5个)
Stream.periodic(const Duration(seconds: 1), (idx) => idx)
    .take(5)
    .listen(print);

// 3. 单次数据流
Stream.value("单次流数据");

3. Stream 核心进阶操作(过滤、转换、合并)

Stream 支持丰富的链式操作,无需手动遍历,极简处理数据流

Stream<int> numStream() async* {
  for (int i = 1; i <= 10; i++) {
    await Future.delayed(const Duration(milliseconds: 300));
    yield i;
  }
}

void streamOperator() {
  numStream()
      .where((value) => value % 2 == 0) // 过滤:只保留偶数
      .map((value) => value * 10) // 转换:数值*10
      .take(3) // 截取前3个数据
      .listen((res) {
        print("处理后数据:$res");
      });
  // 输出:20 40 60
}

4. 单订阅流 vs 多订阅流(高频坑点)

  • 单订阅流(默认):只能被监听一次,重复监听直接报错(async*、StreamController 默认)

  • 多订阅流(广播流):支持多处同时监听,适配全局状态、多组件订阅场景

// 转为广播流,支持多监听
void broadcastStream() {
  var stream = countDownStream().asBroadcastStream();
  // 监听1
  stream.listen((v) => print("监听1:$v"));
  // 监听2
  stream.listen((v) => print("监听2:$v"));
}

5. Flutter 页面实战:StreamBuilder

适配持续数据流 UI:下载进度、蓝牙状态、实时计数、弹幕等,自动响应数据更新

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

  @override
  State<StreamBuilderDemo> createState() => _StreamBuilderDemoState();
}

class _StreamBuilderDemoState extends State<StreamBuilderDemo> {
  final StreamDemo _demo = StreamDemo();

  @override
  void initState() {
    super.initState();
    _demo.startDownload();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: StreamBuilder<double>(
          stream: _demo.progressStream,
          initialData: 0.0,
          builder: (context, snapshot) {
            double progress = snapshot.data ?? 0;
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                LinearProgressIndicator(value: progress),
                const SizedBox(height: 20),
                Text("下载进度:${(progress * 100).toStringAsFixed(0)}%"),
              ],
            );
          },
        ),
      ),
    );
  }

  @override
  void dispose() {
    _demo._progressCtrl.close(); // 页面销毁关闭流,防内存泄漏
    super.dispose();
  }
}

四、Future vs Stream 核心区别(一张表彻底分清)

对比维度

Future

Stream

数据次数

单次结果,一次完成

多次持续推送,流式输出

生命周期

完成/报错即结束

可持续运行,手动关闭销毁

核心关键字

async、await、return

async*、yield、listen、controller

UI 组件

FutureBuilder

StreamBuilder

典型场景

接口请求、文件读取、单次IO

蓝牙广播、进度条、WebSocket、实时状态

资源消耗

低,自动释放

较高,需手动关闭防泄漏

五、高频实战场景选型指南

优先用 Future 的场景

  • 单次网络 GET/POST 请求

  • 本地文件读写、SP 存储读取

  • 单次数据库查询、新增、修改

  • 一次性弹窗异步校验、授权请求

优先用 Stream 的场景

  • BLE 蓝牙持续扫描、设备状态监听

  • 文件下载/上传实时进度监听

  • WebSocket、MQTT 实时消息推送

  • 实时计时器、计数器、日志打印

  • 全局状态订阅、多组件实时同步数据

六、高频坑点与避坑方案

Future 避坑

  • 忘记异常捕获:async/await 必须搭配 try/catch,否则异常崩溃

  • 滥用串行执行:无依赖任务不用 await 串行,改用 Future.wait 并行提速

  • FutureBuilder 重复请求:build 重建会重复触发 Future,需在 State 中缓存 Future 实例

Stream 避坑

  • 流未关闭导致内存泄漏:页面销毁必须 close StreamController、取消监听

  • 单流多监听报错:多组件订阅务必转为 broadcast 广播流

  • 无限流死循环:定时/循环流必须设置终止条件,避免后台无限推送

  • 未处理异常:listen 必须配置 onError,防止流异常导致页面崩溃

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐