You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 19:52:34