FutureBuilder — 是 Flutter 中的一个 widget,它根据从传入的 Future 获取的 AsyncSnapshot 的当前状态自动重建其界面。与 await 后手动调用 setState 不同,FutureBuilder 提供了声明式方法:它在首次渲染时订阅 Future,并在每次状态更改(加载、错误或数据就绪)时调用 builder 函数。根据 Flutter API Reference (2026),FutureBuilder 对于从网络加载数据、从数据库读取以及任何 UI 需要显示加载指示器、错误消息或就绪内容的异步操作特别有用。
要点
FutureBuilder — 是来自 widgets 包的 Flutter 内置 widget,它接受 Future<T> 和一个 builder 函数。当 Future 状态发生变化(执行中、成功完成、出错完成)时,FutureBuilder 会自动重建 UI,用新的 AsyncSnapshot 调用 builder。这消除了通过 setState 和标志手动管理加载状态的需要。
与处理数据流(Stream)的 StreamBuilder 不同,FutureBuilder 专为一次性异步操作设计:HTTP 请求、文件读取、数据库查询。FutureBuilder 自行管理对 Future 的订阅:在首次构建时启动 Future 并跟踪其完成。当 widget 被销毁时,FutureBuilder 不会取消 Future — 这是开发者的责任。
根据 Flutter Cookbook (2026),FutureBuilder 推荐用于异步操作在屏幕初始化时仅运行一次的情况。对于重复操作或数据流,请使用 StreamBuilder。两个 widget 都遵循相同的 Reactive UI 模式,但 FutureBuilder 针对一次性请求进行了优化。
FutureBuilder 的内部实现通过 Future.then 和 catchError 订阅 Future。启动时,FutureBuilder 将 connectionState 设置为 ConnectionState.waiting,并用空数据调用 builder。成功完成时,connectionState 变为 ConnectionState.done 并包含数据。出错时,snapshot.error 填充错误对象。每次更改都会触发 widget 的重建。
AsyncSnapshot — 是一个容器对象,FutureBuilder 在每次状态更改时将其传递给 builder 函数。它包含有关异步操作当前状态的所有信息:加载是否正在进行、收到了哪些数据、是否发生了错误。理解 AsyncSnapshot 是正确使用 FutureBuilder 构建 UI 的关键。
| 属性 | 类型 | 描述 |
|---|---|---|
| connectionState | ConnectionState | 当前连接状态(none、waiting、active、done) |
| data | T? | 从 Future 接收的数据(完成前或出错时为 null) |
| error | Object? | 如果 Future 因异常完成,则为错误对象 |
| hasData | bool | 如果 data 不为 null 且状态为 ConnectionState.done,则为 true |
| hasError | bool | 如果 Future 出错完成,则为 true |
枚举 ConnectionState 确定异步操作的阶段。None — Future 尚未启动时的初始状态(很少使用,通常在没有 initialData 的首次构建时)。Waiting — Future 正在执行,数据尚未收到。Active — 仅由 StreamBuilder 用于部分数据的流。Done — Future 已完成,数据通过 snapshot.data 或错误通过 snapshot.error 可用。
在 builder 函数中正确处理所有 AsyncSnapshot 状态是生产代码的强制性要求。如果您不处理 waiting 状态,用户在加载期间将看到空白屏幕。如果您不处理 hasError,用户将收到没有解释的 Exception。推荐的模式:检查 hasError → 检查 hasData → 默认显示加载。
FutureBuilder 可以在几种标准模式中使用,每种模式解决特定的任务。让我们看看主要场景:初始化时加载数据、带缓存的加载、并行请求和带重试的错误处理。
最常见的模式 — 在 StatefulWidget 或 StatelessWidget 的 build 方法中使用 FutureBuilder。Future 从 initState 传递或直接在 build 中创建。重要的是不要在每次重建时在 build 方法中创建 Future — 这会导致重复请求。使用存储在 State 字段中的 Future。
为防止重复请求,FutureBuilder 可以与 CachedNetworkImage 或本地缓存结合使用。首次加载后,数据保存在内存或 SharedPreferences 中,FutureBuilder 立即显示缓存的数据,同时从网络并行更新它们。这通过即时响应改善了用户体验。
根据 pub.dev (2026),缓存对于图片和数据列表尤其重要。FutureBuilder 与 CachedNetworkImageProvider 自动显示缓存的图片,如果没有缓存,则显示加载指示器,随后显示下载的文件。
FutureBuilder 和通过 setState 手动管理状态 — Flutter 中异步 UI 的两种方法。各有优缺点。选择取决于屏幕的复杂性和异步操作的数量。
FutureBuilder 胜在简单:不需要声明加载状态、数据和错误的字段 — 一切都通过 AsyncSnapshot 管理。它适用于只有一个异步操作的简单屏幕(一个 HTTP 请求、数据库读取)。然而,当一个屏幕上有 5 个以上的异步操作时,FutureBuilder 会造成过度嵌套 — 形成了一个由嵌套 FutureBuilder 组成的 “金字塔”。
setState 加上手动状态标志在复杂逻辑中提供了更多的控制和可读性。对于具有多个依赖请求的屏幕(加载用户 → 加载其订单 → 加载订单详情)最好使用带有 ChangeNotifier 或 Bloc 的 setState。根据 Flutter State Management Guide (2026),对于复杂场景建议使用 Riverpod 或 Bloc 而不是 FutureBuilder,因为它们提供了更好的逻辑和表示分离。
让我们来看一个使用 FutureBuilder 从 REST API 加载用户列表的实践示例。代码演示了所有三种 AsyncSnapshot 状态(加载、错误和数据就绪)的正确处理。
class UserListPage extends StatefulWidget {
const UserListPage({super.key});
@override
State<UserListPage> createState() => _UserListPageState();
}
class _UserListPageState extends State<UserListPage> {
final Future<List<User>> usersFuture = UserRepository().fetchUsers();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Users')),
body: FutureBuilder<List<User>>(
future: usersFuture,
builder: (context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text('Error: ${snapshot.error}'),
],
),
);
}
if (snapshot.hasData) {
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(backgroundImage: NetworkImage(users[index].avatarUrl)),
title: Text(users[index].name),
subtitle: Text(users[index].email),
);
},
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
在示例中,FutureBuilder 处理所有三种状态。出错时,显示带错误消息的图标。成功加载时 — 显示带头像和名称的 ListView。加载期间 — 显示 CircularProgressIndicator。Future 被声明为类字段,防止在重建时重复调用。这种模式覆盖了 90% 的 FutureBuilder 在移动应用中的使用场景。
常见问题
FutureBuilder 在每次 Future 状态更改时调用 builder:第一次在创建时(connectionState: none 或 waiting),第二次在完成时(connectionState: done)。如果父 widget 重建,FutureBuilder 也会重建。为防止重复调用,请确保 Future 在 build 方法之外创建 — 否则每次 build 调用都会创建一个新的 Future。
将 Future 保存在 StatefulWidget 的字段中(在 initState 中)或使用记忆化。如果 Future 在 build 方法内部创建,每次 build 调用都会创建一个新的 Future,FutureBuilder 将重新启动异步操作。对于 StatelessWidget,使用 cached_future 包或 keep-alive widget,使 Future 无论重建多少次都只执行一次。
FutureBuilder 专为一次性异步操作设计(一个 HTTP 请求、一次数据库读取)。StreamBuilder 处理数据流,这些流可以随时间发出多个值(聊天、价格更新、地理位置)。StreamBuilder 支持 ConnectionState.active 用于部分数据,而 FutureBuilder 只支持 waiting 和 done。
对于多个并行 Future,使用 Future.wait 并将结果传递给一个 FutureBuilder。Future.wait 接受一个 Future 列表并返回 Future<List> — 当所有 Future 都完成时,builder 收到结果数组。对于顺序请求,使用单个 Future 中的 Future.then 链或嵌套 FutureBuilder(可读性较差)。替代方案 — 使用带有 AsyncValue 的 riverpod 包处理多个异步状态。
FutureBuilder 不会自动取消 Future。要取消,请使用 async 包中的 CancelableOperation 或通过 State 中的 cancelled 标志使用自己的机制。在 dispose() 中设置标志,并在 Future 完成后在调用 setState 之前检查它。或者,使用带有 AutoDispose 的 riverpod 包,它在离开屏幕时自动取消异步操作。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。