Flutter中FirebaseAuthService传递additionalUserInfo至User模型求助
解决方案:处理Firebase Auth中的新用户引导逻辑
针对你遇到的两个问题,核心原因是onAuthStateChanged流和currentUser方法无法直接获取AdditionalUserInfo——这个信息只有在首次登录通过signInWithCredential等方法拿到AuthResult时才能获取。所以我们需要把isNewUser的值持久化存储,之后从存储中读取来填充User模型。下面是具体的实现方案:
1. 拆分User模型的创建逻辑,添加持久化存储
我们需要两个函数分别处理两种场景:
- 从
AuthResult创建User(此时能拿到isNewUser,并保存到存储) - 从
FirebaseUser创建User(从存储中读取之前保存的isNewUser值)
这里以SharedPreferences本地存储为例(如果需要跨设备同步,可以换成Firestore,逻辑类似):
首先添加shared_preferences依赖到pubspec.yaml,然后修改FirebaseAuthService:
import 'package:firebase_auth/firebase_auth.dart'; import 'package:shared_preferences/shared_preferences.dart'; // 导入你的User模型 import 'path/to/user_model.dart'; class FirebaseAuthService { final FirebaseAuth _firebaseAuth = FirebaseAuth.instance; // 辅助方法:保存isNewUser到本地存储 Future<void> _saveIsNewUser(String uid, bool isNew) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool('user_$uid_isNew', isNew); } // 辅助方法:从本地存储读取isNewUser Future<bool> _getIsNewUser(String uid) async { final prefs = await SharedPreferences.getInstance(); // 默认返回false:老用户会有已保存的值,新用户登录时会覆盖这个值 return prefs.getBool('user_$uid_isNew') ?? false; } // 从FirebaseUser创建User(异步读取存储的isNew值) Future<User> _userFromFirebase(FirebaseUser user) async { if (user == null) return null; final isNew = await _getIsNewUser(user.uid); return User( uid: user.uid, email: user.email, photoUrl: user.photoUrl, displayName: user.displayName, isNew: isNew, ); } // 从AuthResult创建User,并保存isNewUser到存储 Future<User> _userFromAuthResult(AuthResult authResult) async { final user = authResult.user; if (user == null) return null; final isNew = authResult.additionalUserInfo.isNewUser; await _saveIsNewUser(user.uid, isNew); return User( uid: user.uid, email: user.email, photoUrl: user.photoUrl, displayName: user.displayName, isNew: isNew, ); } // 修复onAuthStateChanged:使用asyncMap处理异步函数 Stream<User> get onAuthStateChanged { // asyncMap允许我们在流中处理异步操作 return _firebaseAuth.onAuthStateChanged.asyncMap(_userFromFirebase); } Future<User> signInUsingGoogle() async { final GoogleSignIn googleSignIn = GoogleSignIn(); final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn(); final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication; final AuthCredential credential = GoogleAuthProvider.getCredential( idToken: googleSignInAuthentication.idToken, accessToken: googleSignInAuthentication.accessToken, ); final AuthResult authResult = await _firebaseAuth.signInWithCredential(credential); return _userFromAuthResult(authResult); } Future<User> signInUsingFacebook() async { final FacebookLogin facebookLogin = FacebookLogin(); final FacebookLoginResult facebookLoginResult = await facebookLogin.logIn(['email']); if (facebookLoginResult.status != FacebookLoginStatus.loggedIn) { print(facebookLoginResult.status == FacebookLoginStatus.cancelledByUser ? "Cancelled by user" : "Error signing using facebook"); return null; } final accessToken = facebookLoginResult.accessToken.token; final facebookAuthcred = FacebookAuthProvider.getCredential(accessToken: accessToken); final AuthResult authResult = await _firebaseAuth.signInWithCredential(facebookAuthcred); return _userFromAuthResult(authResult); } Future<User> currentUser() async { final FirebaseUser user = await _firebaseAuth.currentUser(); // 调用异步的_userFromFirebase,从存储读取isNew值 return _userFromFirebase(user); } Future<void> signOut() async { // 可选:退出登录时可以清除本地存储的isNew值(根据业务需求决定) // final user = await _firebaseAuth.currentUser(); // if (user != null) { // final prefs = await SharedPreferences.getInstance(); // await prefs.remove('user_${user.uid}_isNew'); // } return _firebaseAuth.signOut(); } }
2. 调整AuthWidget的逻辑
因为onAuthStateChanged现在返回的是Stream<User>(异步处理后的),你的AuthWidget不需要做太大改动,原逻辑可以直接复用:
class AuthWidget extends StatelessWidget { const AuthWidget({Key key, @required this.userSnapshot}) : super(key: key); final AsyncSnapshot<User> userSnapshot; @override Widget build(BuildContext context) { if (userSnapshot.connectionState == ConnectionState.active) { if (userSnapshot.hasData) { return userSnapshot.data.isNew ? OnBoardingPage() : RootWidget(); } else { return SignInPageBuilder(); } } return const Scaffold( body: Center( child: CircularProgressIndicator(), ) ); } }
关键解决思路说明
- 问题1解决:原来的
map方法只能处理同步函数,我们换成asyncMap来支持异步的_userFromFirebase函数,因为需要从存储读取isNew值。同时拆分了创建User的逻辑,分别处理有AuthResult和无AuthResult的场景。 - 问题2解决:
currentUser方法现在调用_userFromFirebase,从本地存储读取之前登录时保存的isNew值,避免了设置默认值的不可扩展性——新用户登录时我们会主动保存isNew: true,老用户则会读取到之前保存的false。
如果需要跨设备同步isNew状态,可以把SharedPreferences换成Firestore:在登录时将isNewUser写入用户的Firestore文档,之后从文档中读取即可,逻辑和上面一致。
内容的提问来源于stack exchange,提问作者Vedant Rathore
相关产品推荐
相关产品推荐

