Flutter使用Firebase Auth登录后提前跳转主页导致SharedPreferences取值为空如何解决
问题根因
你当前的逻辑中,authStateChanges 流会在Firebase Auth登录校验完成后立即返回用户对象,此时拉取Firestore用户文档、写入SharedPreferences的异步操作还未执行完成,路由直接跳转到首页后读取language字段自然为空。
可行解决方案
下面提供3种可落地的方案,可根据你的项目改造量选择:
方案1:调整主动登录场景的跳转逻辑(改造成本最低)
- 主动点击登录按钮的场景下,不要依赖
authStateChanges的流触发跳转,等所有登录侧的异步操作全部完成后手动跳转:
// 登录按钮点击逻辑 UserCredential userCredential = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password); User user = userCredential.user; DocumentSnapshot snapshot = await DatabaseServices().getUserData(user.uid); await UserPreferences().setUser(snapshot['username'], email, snapshot['language']); // 所有操作完成后再跳首页,替换原来的流跳转逻辑 if(mounted) Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=> Home()));
- 冷启动(APP杀死后重新打开)场景补充处理:在路由判断前加一层校验,如果有Firebase用户但SharedPreferences里没有
language,先拉取用户数据再跳转,期间显示加载页即可。
方案2:改造Provider状态为复合用户状态(最优雅的长期方案)
- 定义包含业务所需用户字段的复合类:
class AppUser { final User? firebaseUser; final String? username; final String? language; final String? email; AppUser({this.firebaseUser, this.username, this.language, this.email}); }
- 替换原来的StreamProvider,返回组装完成的完整用户状态:
StreamProvider<AppUser>( create: (context) async* { await for (User? user in context.read<Authentication>().authStateChanges) { if (user == null) { yield AppUser(); continue; } // 有登录用户时先拉取用户数据再发射状态 DocumentSnapshot snapshot = await DatabaseServices().getUserData(user.uid); await UserPreferences().setUser(snapshot['username'], user.email!, snapshot['language']); yield AppUser( firebaseUser: user, username: snapshot['username'], language: snapshot['language'], email: user.email, ); } }, initialData: AppUser(), )
- 路由判断逻辑调整为只有完整用户数据就绪才跳首页:
final appUser = context.watch<AppUser>(); if (appUser.firebaseUser != null && appUser.language != null) { return Home(); } else if (appUser.firebaseUser != null) { // 用户已登录但数据还在拉取,显示加载页 return Scaffold(body: Center(child: CircularProgressIndicator())); } return LoginPage();
方案3:首页增加兜底逻辑(临时兼容方案)
- 无需修改登录和路由逻辑,只改首页
getFeed方法,判空时主动拉取用户数据:
Future getFeed(HomePostNotifier feedNotifier) async { hasMore = true; SharedPreferences prefs = await SharedPreferences.getInstance(); String? language = prefs.getString('language'); // 兜底:如果language为空,主动拉取当前登录用户的信息 if (language == null) { User? user = FirebaseAuth.instance.currentUser; if (user != null) { DocumentSnapshot snapshot = await DatabaseServices().getUserData(user.uid); await UserPreferences().setUser(snapshot['username'], user.email!, snapshot['language']); language = snapshot['language']; } } await DatabaseServices().getHomeFeed(feedNotifier, language, _pageLimit); }
内容的提问来源于stack exchange,提问作者Khaled Shazbek
相关产品推荐
相关产品推荐

