Flutter中Firebase谷歌登录后UID为空致分数操作失败问题
问题分析
你的核心问题是GoogleSignInProvider中的_user变量没有与Firebase Auth的全局状态保持同步:
- 仅在
googleLogin()主动登录时手动设置_user,但当App重启、用户自动登录(Firebase默认持久化登录状态),或者Auth状态发生其他变化时,_user会保持为null,导致后续调用updateScore()等方法时拿不到UID。 - 另外,
User get user => _user!的强制解包存在崩溃风险,一旦_user为null会直接抛出异常。
解决方案
通过以下步骤确保_user始终与Firebase Auth的当前用户状态同步:
1. 初始化时同步当前登录用户
在Provider的构造函数中,直接获取Firebase Auth的当前用户,覆盖初始的_user值。
2. 监听Auth状态变化
使用Firebase Auth的authStateChanges()流,实时监听用户登录/登出状态,自动更新_user并通知UI刷新。
3. 安全处理用户 getter
将强制解包的getter改为返回User?,避免运行时崩溃。
修改后的完整代码
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; class GoogleSignInProvider extends ChangeNotifier { final googleSignIn = GoogleSignIn(); final FirebaseAuth _auth = FirebaseAuth.instance; // 单独声明Auth实例 User? _user; // 安全返回用户,避免强制解包 User? get user => _user; GoogleSignInProvider() { // 构造函数中初始化当前用户 _user = _auth.currentUser; // 监听Auth状态变化,实时更新_user _auth.authStateChanges().listen((User? newUser) { _user = newUser; notifyListeners(); }); } Future<void> googleLogin() async { try { final googleUser = await googleSignIn.signIn(); if (googleUser == null) return; final googleAuth = await googleUser.authentication; final credential = GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); final userCredential = await _auth.signInWithCredential(credential); // 这里不需要手动设置_user,因为authStateChanges流会自动更新 print("User UID: ${userCredential.user?.uid}"); if (userCredential.user == null) return; // 保存用户数据到Firestore await createUserDocument(userCredential.user!.uid, { "name": userCredential.user!.displayName, "email": userCredential.user!.email, "profilePicture": userCredential.user!.photoURL, "GINO": "Moi et tout ceux qui le veulent", "Lasagne": "chaude", }); } catch (e) { print(e.toString()); } notifyListeners(); } Future<void> createUserDocument(String userId, Map<String, dynamic> userData) async { final userCollection = FirebaseFirestore.instance.collection("Users"); final userDocument = userCollection.doc(userId); // 使用setOptions(merge: true)避免覆盖已存在的用户数据 await userDocument.set(userData, SetOptions(merge: true)); // 初始化分数(仅当分数不存在时设置) await userDocument.set({"score": FieldValue.increment(0)}, SetOptions(merge: true)); print("User document created/updated successfully."); } Future<void> updateScore(int newScore) async { final userId = _user?.uid; if (userId == null) { print("用户未登录,无法更新分数"); return; } final userDocument = FirebaseFirestore.instance.collection("Users").doc(userId); print("User UID before score update: $userId"); await userDocument.update({"score": newScore}); print("分数更新成功"); } Future<int?> getUserScore() async { final userId = _user?.uid; if (userId == null) { print("用户未登录,无法获取分数"); return null; } final userDocument = FirebaseFirestore.instance.collection("Users").doc(userId); print("User UID before score retrieval: $userId"); try { final userData = await userDocument.get(); return userData.data()?["score"] as int?; } catch (e) { print("获取分数失败: $e"); return null; } } Future<void> updateScoretest(int newScore) async { final userId = _user?.uid; if (userId == null) { print("用户未登录,无法测试更新分数"); return; } print("WTF $userId"); final userDocument = FirebaseFirestore.instance.collection("Users").doc(userId); await userDocument.update({"score": newScore}); print("测试分数更新成功"); } Future<void> logout() async { await googleSignIn.signOut(); await _auth.signOut(); // 这里不需要手动设置_user为null,authStateChanges流会自动处理 notifyListeners(); } void updateFlashcardSwipeData({required String building, required flashcardId, required bool isCorrect}) {} }
额外注意事项
- 确保你的Firebase项目已正确配置Google登录,并且
google-services.json(Android)/GoogleService-Info.plist(iOS)已正确放置。 - 使用
SetOptions(merge: true)调用set()方法,避免在用户重新登录时覆盖已存在的分数等数据。 - 在UI层使用
Consumer<GoogleSignInProvider>或Provider.of<GoogleSignInProvider>监听用户状态变化,确保UI能实时响应登录/登出状态。
内容的提问来源于stack exchange,提问作者Adam Messier
相关产品推荐
相关产品推荐

