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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 08:47:42