FutureBuilder — 是什么,在 Flutter 中与 Future 工作

作者: IT Sectr 发布日期: 2026-07-02 阅读时间: 8 分钟

FutureBuilder — 是 Flutter 中的一个 widget,它根据从传入的 Future 获取的 AsyncSnapshot 的当前状态自动重建其界面。与 await 后手动调用 setState 不同,FutureBuilder 提供了声明式方法:它在首次渲染时订阅 Future,并在每次状态更改(加载、错误或数据就绪)时调用 builder 函数。根据 Flutter API Reference (2026),FutureBuilder 对于从网络加载数据、从数据库读取以及任何 UI 需要显示加载指示器、错误消息或就绪内容的异步操作特别有用。

要点

  • FutureBuilder — 用于通过 AsyncSnapshot(none、waiting、active、done)基于 Future 状态构建 UI 的 Flutter widget
  • AsyncSnapshot — 包含异步操作当前状态的对象:connectionState、data 和 error
  • builder — 在 Future 每次状态更改时调用的回调函数,用于重建 UI
  • 错误处理 — AsyncSnapshot.hasError 允许在异步操作失败时显示备用界面
  • ConnectionState — 包含四个值的枚举:none(无操作)、waiting(等待中)、active(流式)、done(已完成)

Flutter 中的 FutureBuilder 是什么

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 的工作原理

FutureBuilder 的内部实现通过 Future.then 和 catchError 订阅 Future。启动时,FutureBuilder 将 connectionState 设置为 ConnectionState.waiting,并用空数据调用 builder。成功完成时,connectionState 变为 ConnectionState.done 并包含数据。出错时,snapshot.error 填充错误对象。每次更改都会触发 widget 的重建。

AsyncSnapshot:状态和属性

AsyncSnapshot — 是一个容器对象,FutureBuilder 在每次状态更改时将其传递给 builder 函数。它包含有关异步操作当前状态的所有信息:加载是否正在进行、收到了哪些数据、是否发生了错误。理解 AsyncSnapshot 是正确使用 FutureBuilder 构建 UI 的关键。

属性类型描述
connectionStateConnectionState当前连接状态(none、waiting、active、done)
dataT?从 Future 接收的数据(完成前或出错时为 null)
errorObject?如果 Future 因异常完成,则为错误对象
hasDatabool如果 data 不为 null 且状态为 ConnectionState.done,则为 true
hasErrorbool如果 Future 出错完成,则为 true

ConnectionState:异步操作的四种状态

枚举 ConnectionState 确定异步操作的阶段。None — Future 尚未启动时的初始状态(很少使用,通常在没有 initialData 的首次构建时)。Waiting — Future 正在执行,数据尚未收到。Active — 仅由 StreamBuilder 用于部分数据的流。Done — Future 已完成,数据通过 snapshot.data 或错误通过 snapshot.error 可用。

在 builder 函数中正确处理所有 AsyncSnapshot 状态是生产代码的强制性要求。如果您不处理 waiting 状态,用户在加载期间将看到空白屏幕。如果您不处理 hasError,用户将收到没有解释的 Exception。推荐的模式:检查 hasError → 检查 hasData → 默认显示加载。

FutureBuilder 的使用模式

FutureBuilder 可以在几种标准模式中使用,每种模式解决特定的任务。让我们看看主要场景:初始化时加载数据、带缓存的加载、并行请求和带重试的错误处理。

屏幕初始化时加载数据

最常见的模式 — 在 StatefulWidget 或 StatelessWidget 的 build 方法中使用 FutureBuilder。Future 从 initState 传递或直接在 build 中创建。重要的是不要在每次重建时在 build 方法中创建 Future — 这会导致重复请求。使用存储在 State 字段中的 Future。

带缓存和刷新的加载

为防止重复请求,FutureBuilder 可以与 CachedNetworkImage 或本地缓存结合使用。首次加载后,数据保存在内存或 SharedPreferences 中,FutureBuilder 立即显示缓存的数据,同时从网络并行更新它们。这通过即时响应改善了用户体验。

根据 pub.dev (2026),缓存对于图片和数据列表尤其重要。FutureBuilder 与 CachedNetworkImageProvider 自动显示缓存的图片,如果没有缓存,则显示加载指示器,随后显示下载的文件。

FutureBuilder 与 setState:如何选择

FutureBuilder 和通过 setState 手动管理状态 — Flutter 中异步 UI 的两种方法。各有优缺点。选择取决于屏幕的复杂性和异步操作的数量。

FutureBuilder 胜在简单:不需要声明加载状态、数据和错误的字段 — 一切都通过 AsyncSnapshot 管理。它适用于只有一个异步操作的简单屏幕(一个 HTTP 请求、数据库读取)。然而,当一个屏幕上有 5 个以上的异步操作时,FutureBuilder 会造成过度嵌套 — 形成了一个由嵌套 FutureBuilder 组成的 “金字塔”。

setState 加上手动状态标志在复杂逻辑中提供了更多的控制和可读性。对于具有多个依赖请求的屏幕(加载用户 → 加载其订单 → 加载订单详情)最好使用带有 ChangeNotifier 或 Bloc 的 setState。根据 Flutter State Management Guide (2026),对于复杂场景建议使用 Riverpod 或 Bloc 而不是 FutureBuilder,因为它们提供了更好的逻辑和表示分离。

FutureBuilder 从网络加载数据示例

让我们来看一个使用 FutureBuilder 从 REST API 加载用户列表的实践示例。代码演示了所有三种 AsyncSnapshot 状态(加载、错误和数据就绪)的正确处理。

dart
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 多次调用 builder?

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 与 StreamBuilder 有何不同?

FutureBuilder 专为一次性异步操作设计(一个 HTTP 请求、一次数据库读取)。StreamBuilder 处理数据流,这些流可以随时间发出多个值(聊天、价格更新、地理位置)。StreamBuilder 支持 ConnectionState.active 用于部分数据,而 FutureBuilder 只支持 waiting 和 done。

如何将 FutureBuilder 与多个 Future 一起使用?

对于多个并行 Future,使用 Future.wait 并将结果传递给一个 FutureBuilder。Future.wait 接受一个 Future 列表并返回 Future<List> — 当所有 Future 都完成时,builder 收到结果数组。对于顺序请求,使用单个 Future 中的 Future.then 链或嵌套 FutureBuilder(可读性较差)。替代方案 — 使用带有 AsyncValue 的 riverpod 包处理多个异步状态。

如何在离开屏幕时取消 Future?

FutureBuilder 不会自动取消 Future。要取消,请使用 async 包中的 CancelableOperation 或通过 State 中的 cancelled 标志使用自己的机制。在 dispose() 中设置标志,并在 Future 完成后在调用 setState 之前检查它。或者,使用带有 AutoDispose 的 riverpod 包,它在离开屏幕时自动取消异步操作。

总结

  • FutureBuilder — 用于通过 AsyncSnapshot(waiting、done、error)基于 Future 状态声明式构建 UI 的 Flutter widget
  • AsyncSnapshot — 包含 connectionState、data 和 error 的容器;必须正确处理异步操作的所有状态
  • builder — 具有三个分支的回调:hasError(显示错误)、hasData(显示数据)、default(加载指示器)
  • FutureBuilder 与 setState — FutureBuilder 对于单个操作更简单,带有 Bloc/Riverpod 的 setState 更适合具有多个请求的复杂逻辑
  • 防止重复请求 — Future 必须是 State 字段,不要在 build 方法中创建它,以避免每次重建时重新启动
  • 取消 Future — FutureBuilder 不会在 dispose 时取消 Future;使用 CancelableOperation 或取消标志来防止销毁后调用 setState
  • 多个 Future — 对于并行请求,使用一个 FutureBuilder 配合 Future.wait;对于顺序请求 — 在单个 Future 中使用链

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读