Flutter 异步与同步(2):Flutter中同步异步的使用
一、StreamBuilder/FutureBuilder
在 Flutter 中,StreamBuilder 和 FutureBuilder 都是用于处理异步数据源的常用组件。它们允许你在等待异步操作完成时更新 UI。尽管它们的功能类似,但它们适用于不同的异步数据源类型。
1.1 FutureBuilder
FutureBuilder 用于处理 Future 类型的异步数据源。Future 表示一个将来可能会完成的单一异步操作,例如一次性网络请求。
适用场景
- 当你有一个一次性的异步操作,例如从网络加载数据,执行数据库查询等。
构造函数参数
-
future: 一个 Future 对象,表示要等待的异步操作。
-
builder: 一个函数,构建UI,并根据 Future 的状态来更新UI。
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
home: MyFutureBuilder(),
));
class MyFutureBuilder extends StatelessWidget {
const MyFutureBuilder({super.key});
Future<String> fetchData() async {
await Future.delayed(const Duration(seconds: 5)); // 模拟网络延迟
return 'Hello, FutureBuilder!';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('FutureBuilder Example')),
body: Center(
child: FutureBuilder<String>(
future: fetchData(), // 要等待的异步操作
builder: (BuildContext context, AsyncSnapshot<String> snapshot) { // 构建UI,并根据 Future 的状态来更新UI
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator(); // 圆形进度条
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('Result: ${snapshot.data}');
}
},
),
),
);
}
}
等待 5 秒网络延迟后,可以看到文本显示为 "Hello, FutureBuilder!"。
等待异步操作时的状态图:

5 秒网络延迟后,完成异步操作后的状态图:

1.2 StreamBuilder
StreamBuilder 用于处理 Stream 类型的异步数据源。Stream 表示一系列的异步事件或数据,例如连续的传感器数据、WebSocket 数据等。
适用场景
- 当你有多个异步数据或事件的流,例如实时更新的数据、传感器数据等。
构造函数参数
-
stream: 一个 Stream 对象,表示要监听的异步数据流。
-
builder: 一个函数,构建UI,并根据 Stream 的状态和数据来更新UI。
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
home: MyStreamBuilder(),
));
class MyStreamBuilder extends StatelessWidget {
const MyStreamBuilder({super.key});
Stream<int> counterStream() async* {
for (int i = 0; i < 10; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('StreamBuilder Example')),
body: Center(
child: StreamBuilder<int>(
stream: counterStream(), // 要等待的异步操作
builder: (BuildContext context, AsyncSnapshot<int> snapshot) { // 构建UI,并根据 Future 的状态来更新UI
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (!snapshot.hasData) {
return const Text('No data');
} else {
return Text('Counter: ${snapshot.data}');
}
},
),
),
);
}
}
可以看到数量从 1 到 9,效果图如下所示:

1.3 优缺点
FutureBuilder
优点
- 简单易用: 适合处理一次性异步操作,只需关心数据何时完成,代码逻辑简单明了。
- 轻量: 只处理一次异步数据,因此不需要持续监听或管理数据流,内存和资源消耗较少。
- 减少复杂性: 不需要考虑多次数据更新的处理逻辑,适用于单次数据请求或简单任务。
缺点
数据不可更新: FutureBuilder 只会在 Future 完成时更新一次,无法处理后续的数据变化。如果需要实时更新数据,需要重新触发整个组件的构建。
无法处理长时间操作: 对于需要长时间处理或持续更新的操作不适用,必须使用 StreamBuilder 或其它机制。
StreamBuilder
优点
- 实时更新: 可以处理多次数据更新,每当有新的数据事件时,都会自动更新 UI,适合需要实时响应数据变化的场景。
- 强大的灵活性: 适用于多种异步数据流处理场景,如 WebSocket 连接、传感器数据、用户输入等,具备更高的适应性。
- 多事件处理: 可以监听和响应连续的多个异步事件,提供更丰富的交互体验。
缺点
- 复杂性较高: 由于需要处理多个事件,代码逻辑相对复杂,尤其是在处理错误、空数据或状态变化时,可能需要更多的管理代码。
- 资源消耗大: 持续监听数据流可能会占用更多的内存和CPU资源,尤其是长时间运行的操作。如果处理不当,可能会导致性能问题或资源泄漏。
- 潜在的状态管理问题: 如果不注意数据流的管理和清理,可能会引入内存泄漏或不必要的状态更新。
适用场景
- FutureBuilder: 适用于一次性数据加载场景,如启动时加载配置、单次API请求等。
- StreamBuilder: 适用于需要持续监听和更新UI的场景,如实时聊天、状态监控、数据推送等。
- 选择 FutureBuilder 还是 StreamBuilder 取决于你的需求。如果是一次性任务且不需要后续更新,FutureBuilder 是更简单的选择。如果需要处理多次数据更新或实时响应,StreamBuilder 则提供了更大的灵活性和功能。
二、Future.wait/Future.any
2.1 Future.wait
Future.wait 等待所有提供的 Future 对象完成,并在所有 Future 都完成后返回一个包含每个 Future 结果的列表。如果任何一个 Future 抛出异常,则 Future.wait 会返回第一个抛出的异常。
适用场景
- 当你有多个并发的异步任务,并且需要等待所有任务都完成后再继续执行后续操作。
import 'package:flutter/material.dart';
import 'dart:async';
void main() async {
debugPrint('Start');
Future<int> task1 = Future.delayed(const Duration(seconds: 2), () => 1);
Future<int> task2 = Future.delayed(const Duration(seconds: 3), () => 2);
Future<int> task3 = Future.delayed(const Duration(seconds: 1), () => 3);
List<int> results = await Future.wait([task1, task2, task3]);
debugPrint('Results: $results'); // [1, 2, 3]
debugPrint('End');
}
打印输出:
flutter: Start
flutter: Results: [1, 2, 3]
flutter: End
2.2 Future.any
Future.any 等待提供的 Future 对象中第一个完成的任务,并返回该任务的结果。如果所有 Future 都抛出异常,则 Future.any 会返回最后一个抛出的异常。
适用场景
- 当你有多个并发的异步任务,并且只需要其中任何一个任务完成后就继续执行后续操作。
import 'package:flutter/material.dart';
import 'dart:async';
void main() async {
debugPrint('Start');
Future<int> task1 = Future.delayed(const Duration(seconds: 2), () => 1);
Future<int> task2 = Future.delayed(const Duration(seconds: 3), () => 2);
Future<int> task3 = Future.delayed(const Duration(seconds: 1), () => 3);
int result = await Future.any([task1, task2, task3]);
debugPrint('First completed result: $result'); // 3
debugPrint('End');
}
在这个示例中,Future.any 会在 task3 完成后立即返回结果,因为 task3 是最先完成的任务。打印输出:
flutter: Start
flutter: First completed result: 3
flutter: End
2.3 区别总结
-
行为方式:
Future.wait等待所有Future对象完成,并返回所有结果。Future.any等待第一个完成的Future对象,并返回其结果。
-
错误处理:
Future.wait如果任何一个Future抛出异常,则返回第一个抛出的异常。Future.any如果所有Future都抛出异常,则返回最后一个抛出的异常。
-
适用场景:
Future.wait适用于需要所有并发任务都完成后再处理结果的场景。Future.any适用于只需要其中任何一个任务完成即可继续执行的场景。
2.4 实践中的应用场景
使用 Future.wait
假设你在开发一个应用,需要同时发起多个网络请求,只有在所有请求完成后才能继续处理响应数据。这时你可以使用 Future.wait:
import 'package:flutter/material.dart';
import 'dart:async';
void main() async {
Future<String> fetchUserData() async {
await Future.delayed(const Duration(seconds: 2));
return 'User data';
}
Future<String> fetchOrders() async {
await Future.delayed(const Duration(seconds: 3));
return 'Orders';
}
Future<String> fetchSettings() async {
await Future.delayed(const Duration(seconds: 1));
return 'Settings';
}
debugPrint('Fetching data...');
List<String> results = await Future.wait([
fetchUserData(),
fetchOrders(),
fetchSettings(),
]);
debugPrint('All data fetched: $results');
}
打印输出:
flutter: Fetching data...
flutter: All data fetched: [User data, Orders, Settings]
使用 Future.any
假设你在开发一个应用,需要从多个服务器中获取数据,只要任何一个服务器返回数据即可。这时你可以使用 Future.any:
import 'package:flutter/material.dart';
import 'dart:async';
void main() async {
Future<String> fetchFromServer1() async {
await Future.delayed(const Duration(seconds: 2));
return 'Data from server 1';
}
Future<String> fetchFromServer2() async {
await Future.delayed(const Duration(seconds: 3));
return 'Data from server 2';
}
Future<String> fetchFromServer3() async {
await Future.delayed(const Duration(seconds: 1));
return 'Data from server 3';
}
debugPrint('Fetching data...');
String result = await Future.any([
fetchFromServer1(),
fetchFromServer2(),
fetchFromServer3(),
]);
debugPrint('First data fetched: $result');
}
打印输出:
flutter: Fetching data...
flutter: First data fetched: Data from server 3
三、Future.doWhile/Future.forEach
3.1 Future.doWhile
Future.doWhile 用于执行一个异步循环,直到条件不满足为止。循环体中的每次迭代是异步的,并且需要返回一个 Future<bool>,表示是否继续循环。
用法
- 适用于需要重复执行异步操作,直到某个条件不再满足的场景。
import 'package:flutter/material.dart';
import 'dart:async';
void main() async {
int counter = 0;
debugPrint('Start');
await Future.doWhile(() async {
counter++;
debugPrint('Counter: $counter');
await Future.delayed(const Duration(seconds: 1)); // 模拟异步操作
return counter < 5; // 继续循环直到 counter >= 5
});
debugPrint('End');
}
打印输出:
flutter: Start
flutter: Counter: 1
flutter: Counter: 2
flutter: Counter: 3
flutter: Counter: 4
flutter: Counter: 5
flutter: End
3.2 Future.forEach
Future.forEach 用于对集合中的每个元素执行异步操作。它依次执行每个元素的异步操作,等待当前操作完成后再执行下一个操作。
用法
- 适用于需要对集合中的每个元素执行异步操作的场景。
import 'package:flutter/material.dart';
import 'dart:async';
void main() async {
List<int> numbers = [1, 2, 3, 4, 5];
debugPrint('Start');
await Future.forEach(numbers, (number) async {
debugPrint('Processing number: $number');
await Future.delayed(const Duration(seconds: 1)); // 模拟异步操作
});
debugPrint('End');
}
打印输出:
flutter: Start
flutter: Processing number: 1
flutter: Processing number: 2
flutter: Processing number: 3
flutter: Processing number: 4
flutter: Processing number: 5
flutter: End
3.3 区别总结
- 控制流方式:
Future.doWhile:用于执行一个异步循环,直到条件返回false为止。适合在不知道具体迭代次数的情况下执行重复的异步操作。Future.forEach:用于对集合中的每个元素执行异步操作,适合在知道具体迭代次数的情况下对集合进行异步处理。
- 返回类型:
Future.doWhile:接受一个返回Future<bool>的函数作为参数。Future.forEach:接受一个返回Future<void>的函数作为参数。
- 终止条件:
Future.doWhile:循环的终止条件由函数返回的布尔值决定。Future.forEach:循环的终止条件是集合中所有元素都处理完毕。
- 应用场景:
Future.doWhile:适用于需要重复执行异步操作,直到某个条件不再满足的情况。Future.forEach:适用于需要对集合的每个元素执行异步操作的情况。
四、Future异常处理
Flutter 和 Dart 提供了几种方法来捕获和处理 Future 异常,包括使用 try-catch 语句、catchError 方法以及在异步函数中处理异常。
4.1 try-catch
import 'package:flutter/material.dart';
import 'dart:async';
Future<void> fetchData() async {
try {
await Future.delayed(const Duration(seconds: 2));
throw Exception('Failed to fetch data'); // 模拟异常
} catch (e) {
debugPrint('Caught an exception: $e');
}
}
void main() async {
await fetchData();
debugPrint('Done');
}
打印输出:
flutter: Caught an exception: Exception: Failed to fetch data
flutter: Done
4.2 catchError
catchError 方法需要一个回调函数,该函数接收异常作为参数。
import 'package:flutter/material.dart';
import 'dart:async';
Future<void> fetchData() {
return Future.delayed(const Duration(seconds: 2))
.then((_) {
throw Exception('Failed to fetch data'); // 模拟异常
})
.catchError((e) {
debugPrint('Caught an exception: $e');
});
}
void main() async {
await fetchData();
debugPrint('Done');
}
打印输出:
flutter: Caught an exception: Exception: Failed to fetch data
flutter: Done

浙公网安备 33010602011771号