You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何结合FutureBuilder显示CircularProgressIndicator?

解决方案:两种实现异步加载状态的方式

一、使用FutureBuilder实现

核心是把authUser异步方法的Future实例传递给FutureBuilder的future属性,注意不要在build方法内直接调用authUser()(会导致重复执行异步操作),需提前将Future实例存储在State中。

代码示例:

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

  @override
  State<AuthPage> createState() => _AuthPageState();
}

class _AuthPageState extends State<AuthPage> {
  // 提前初始化Future实例,避免build时重复触发异步操作
  late final Future<User?> _authFuture;

  @override
  void initState() {
    super.initState();
    // 初始化异步操作的Future
    _authFuture = authUser();
  }

  // 你的异步验证方法
  Future<User?> authUser() async {
    // 模拟接口请求、用户验证等异步逻辑
    await Future.delayed(const Duration(seconds: 2));
    // 返回用户信息或null(根据实际业务调整)
    return User(id: "user_001", name: "测试用户");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        // here1位置:用FutureBuilder包裹加载状态区域
        child: FutureBuilder<User?>(
          future: _authFuture,
          builder: (context, snapshot) {
            // 加载中状态:显示CircularProgressIndicator
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const CircularProgressIndicator();
            }
            // 加载完成且有数据:执行页面跳转
            else if (snapshot.hasData) {
              // 用addPostFrameCallback避免build过程中调用Navigator引发异常
              WidgetsBinding.instance.addPostFrameCallback((_) {
                Navigator.pushReplacement(
                  context,
                  MaterialPageRoute(builder: (_) => const HomePage()),
                );
              });
              // 跳转前返回空占位
              return const SizedBox.shrink();
            }
            // 加载出错:显示错误提示
            else if (snapshot.hasError) {
              return Text("验证失败:${snapshot.error}");
            }
            // 默认状态:显示触发验证的按钮
            else {
              return ElevatedButton(
                onPressed: () {
                  // 重新触发验证:更新Future实例并刷新状态
                  setState(() {
                    _authFuture = authUser();
                  });
                },
                child: const Text("开始验证"),
              );
            }
          },
        ),
      ),
    );
  }
}

// 模拟用户模型
class User {
  final String id;
  final String name;
  User({required this.id, required this.name});
}

关键要点:

  • 务必将Future实例存储在State变量中,仅在初始化或需要重新触发时赋值,避免build重复执行异步操作。
  • 页面跳转逻辑必须放在addPostFrameCallback中,防止在组件构建阶段操作路由引发异常。

二、使用Riverpod FutureProvider实现

将authUser的异步逻辑封装到FutureProvider中,通过监听Provider状态来切换加载指示器、处理跳转。

代码示例:

// 全局定义FutureProvider
final authUserProvider = FutureProvider<User?>((ref) async {
  // 这里编写用户验证的异步逻辑
  await Future.delayed(const Duration(seconds: 2));
  return User(id: "user_001", name: "测试用户");
});

// 页面组件
class AuthPage extends ConsumerWidget {
  const AuthPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 监听Provider状态
    final authState = ref.watch(authUserProvider);

    return Scaffold(
      body: Center(
        child: authState.when(
          // 加载中状态:显示CircularProgressIndicator(here1位置)
          loading: () => const CircularProgressIndicator(),
          // 出错状态:显示错误信息
          error: (error, stack) => Text("验证失败:$error"),
          // 加载完成:处理数据与跳转
          data: (user) {
            if (user != null) {
              WidgetsBinding.instance.addPostFrameCallback((_) {
                Navigator.pushReplacement(
                  context,
                  MaterialPageRoute(builder: (_) => const HomePage()),
                );
              });
              return const SizedBox.shrink();
            }
            // 用户为空时显示触发验证按钮
            return ElevatedButton(
              onPressed: () {
                // 重新触发异步操作:刷新Provider
                ref.invalidate(authUserProvider);
              },
              child: const Text("开始验证"),
            );
          },
        ),
      ),
    );
  }
}

关键要点:

  • 使用ref.watch监听FutureProvider,通过when方法统一处理加载、出错、完成三种状态。
  • 需要重新执行异步操作时,调用ref.invalidate(authUserProvider)让Provider重新执行逻辑。
  • 跳转逻辑同样需放在addPostFrameCallback中,避免构建阶段操作路由。

补充:async/await基础说明

async标记的方法会返回一个Future对象,代表尚未完成的异步操作;await用于暂停当前方法执行,直到后续的异步操作完成并返回结果。比如你的authUser方法:

Future<User?> authUser() async {
  // 等待异步登录接口返回结果
  final loginResult = await api.login();
  // 返回用户信息
  return loginResult.user;
}

FutureBuilder和Riverpod FutureProvider本质都是监听这个Future的状态(等待、完成、出错),从而动态更新UI。

内容的提问来源于stack exchange,提问作者SugiuraY

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 00:25:26