Flutter中Firebase authStateChanges未等API完成即跳转路由求助
解决Firebase authStateChanges提前跳转,未等待REST API完成的问题
问题核心是Firebase的authStateChanges会在认证成功(比如OTP验证通过)时立即触发,而后续的用户数据保存API是异步操作,导致路由跳转先于API执行完成。以下是几种可行的解决方案:
方案1:组合认证状态与数据同步状态流
创建一个组合流,仅当用户认证成功且数据同步完成时,才触发路由跳转。
步骤1:在AuthService中添加同步状态流
使用BehaviorSubject(需依赖rxdart)或普通StreamController跟踪数据同步状态:
import 'package:rxdart/rxdart.dart'; import 'package:firebase_auth/firebase_auth.dart'; class AuthService { final FirebaseAuth _account = FirebaseAuth.instance; final BehaviorSubject<bool> _userDataSynced = BehaviorSubject.seeded(false); Stream<User?> get authStateChange => _account.authStateChanges(); Stream<bool> get userDataSynced => _userDataSynced.stream; Future<void> verifyOTp(String otp, String verificationId) async { try { PhoneAuthCredential credential = PhoneAuthProvider.credential( verificationId: verificationId, smsCode: otp, ); UserCredential userCredential = await _account.signInWithCredential(credential); // 先执行所有用户数据保存API await createNewVendor(userCredential.user!.uid); // 其他必要的API操作... // 标记数据同步完成 _userDataSynced.add(true); } catch (e) { _userDataSynced.add(false); // 错误处理:弹窗提示、登出Firebase await _account.signOut(); } } // 其他方法... }
步骤2:在main.dart中监听组合流
将认证状态流与同步状态流组合,只有两个条件都满足时才跳转首页:
StreamBuilder( stream: Stream.combineLatest2( authService.authStateChange, authService.userDataSynced, (User? user, bool isSynced) => user != null && isSynced, ), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const SplashScreen(); } return snapshot.data == true ? const HomeScreen() : const LoginScreen(); }, )
方案2:手动控制路由跳转,跳过authStateChanges自动触发
在OTP验证完成后,先执行API操作,完成后手动跳转路由,同时在authStateChanges监听中添加同步状态判断。
步骤1:修改verifyOTp方法
执行API操作完成后手动跳转,并通过本地存储标记同步状态:
import 'package:shared_preferences/shared_preferences.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; Future<void> verifyOTp(String otp, String verificationId, BuildContext context) async { try { PhoneAuthCredential credential = PhoneAuthProvider.credential( verificationId: verificationId, smsCode: otp, ); await FirebaseAuth.instance.signInWithCredential(credential); // 显示加载弹窗,防止用户操作 showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); // 执行数据保存API await createNewVendor(FirebaseAuth.instance.currentUser!.uid); // 其他API操作... // 标记同步完成到本地存储 final prefs = await SharedPreferences.getInstance(); await prefs.setBool('user_data_synced', true); // 关闭弹窗并手动跳转首页 Navigator.pop(context); Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const HomeScreen())); } catch (e) { await FirebaseAuth.instance.signOut(); // 错误提示 ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString()))); } }
步骤2:在main.dart中添加同步状态判断
import 'package:shared_preferences/shared_preferences.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; StreamBuilder<User?>( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const SplashScreen(); } final user = snapshot.data; if (user != null) { return FutureBuilder<bool>( future: SharedPreferences.getInstance().then((prefs) => prefs.getBool('user_data_synced') ?? false), builder: (context, prefsSnapshot) { if (prefsSnapshot.connectionState == ConnectionState.waiting) { return const LoadingScreen(); } return prefsSnapshot.data == true ? const HomeScreen() : const OtpVerificationScreen(); }, ); } else { // 清除同步标记,避免下次登录残留 SharedPreferences.getInstance().then((prefs) => prefs.setBool('user_data_synced', false)); return const LoginScreen(); } }, )
方案3:使用Firebase自定义Claims标记同步状态
通过Firebase用户自定义Claims记录数据同步状态,确保只有同步完成后才跳转。
步骤1:在verifyOTp中设置自定义Claim
import 'package:firebase_auth/firebase_auth.dart'; Future<void> verifyOTp(String otp, String verificationId) async { try { PhoneAuthCredential credential = PhoneAuthProvider.credential( verificationId: verificationId, smsCode: otp, ); UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential); final user = userCredential.user!; // 执行数据保存API await createNewVendor(user.uid); // 设置自定义Claim标记同步完成 await FirebaseAuth.instance.setCustomUserClaims(user.uid, {'dataSynced': true}); // 刷新用户令牌,使新Claim生效 await user.reload(); } catch (e) { await FirebaseAuth.instance.signOut(); // 错误处理 } }
步骤2:在main.dart中检查自定义Claim
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; StreamBuilder<User?>( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const SplashScreen(); } final user = snapshot.data; if (user != null) { return FutureBuilder<IdTokenResult>( future: user.getIdTokenResult(), builder: (context, tokenSnapshot) { if (tokenSnapshot.connectionState == ConnectionState.waiting) { return const LoadingScreen(); } bool isSynced = tokenSnapshot.data?.claims?['dataSynced'] ?? false; return isSynced ? const HomeScreen() : const OtpVerificationScreen(); }, ); } else { return const LoginScreen(); } }, )
内容的提问来源于stack exchange,提问作者abhi jain
相关产品推荐
相关产品推荐

