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
相关产品推荐
相关产品推荐

