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

Flutter中Apple登录无法获取用户名问题求助

问题:Apple登录无法获取用户名(首次/再次登录均失败)

使用sign_in_with_apple: ^5.0.0和firebase_auth: ^4.12.1实现Apple登录注册,但无论首次注册还是再次登录,都无法从Apple登录获取用户名。尝试通过代码处理用户名更新,但始终无法拿到正确的用户名,求排查代码问题或告知首次注册时获取用户名的正确方式。

实现代码:

Future signInWithApple() async {
_hasError = false;
notifyListeners();
bool canAppleSignIn = await SignInWithApple.isAvailable();

if (canAppleSignIn == true) {
  // executing our authentication
  try {
    final credential = await SignInWithApple.getAppleIDCredential(
      scopes: [
        AppleIDAuthorizationScopes.email,
        AppleIDAuthorizationScopes.fullName,
      ],
    );

    final oauthCredential = OAuthProvider("apple.com").credential(
      idToken: credential.identityToken,
    );
    final UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);

    if (userCredential.user?.displayName == null ||
        (userCredential.user?.displayName != null && userCredential.user!.displayName!.isEmpty)) {
      final fixDisplayNameFromApple = [
        credential.givenName ?? '',
        credential.familyName ?? '',
      ].join(' ').trim();
      await userCredential.user?.updateDisplayName(fixDisplayNameFromApple);
    }
    if (userCredential.user?.email == null ||
        (userCredential.user?.email != null && userCredential.user!.email!.isEmpty)) {
      await userCredential.user?.updateEmail(credential.email ?? '');
    }

    user = FireStoreUser(
        email: userCredential.user?.email,
        imageUrl: userCredential.user?.photoURL ?? "https://winaero.com/blog/wp-content/uploads/2017/12/User-icon-256-blue.png",
        name: userCredential.user?.displayName,
        provider: "APPLE",
        uid: userCredential.user!.uid,
      );

    await saveDataToSharedPreferences();

    notifyListeners();
  } on FirebaseAuthException catch (e) {
    switch (e.code) {
      case "account-exists-with-different-credential":
        _errorCode =
            "You already have an account with us. Use correct provider";
        _hasError = true;
        notifyListeners();
        break;

      case "null":
        _errorCode = "Some unexpected error while trying to sign in";
        _hasError = true;
        notifyListeners();
        break;
      default:
        _errorCode = e.toString();
        _hasError = true;
        notifyListeners();
    }
  }
} else {
  _hasError = true;
  notifyListeners();
}
}

解决方案

核心原因:Apple授权规则限制

Apple 仅在用户首次授权应用时,才会返回givenName、familyName和email;后续用户再次登录时,Apple不会再返回这些敏感信息,只会返回用户标识。你的代码未考虑该规则,同时存在Firebase用户实例未刷新的问题。

代码问题排查与修复

  1. Firebase用户实例未自动刷新:调用updateDisplayName后,userCredential.user对象不会实时更新,直接读取会拿到旧值。
  2. 未持久化用户名:首次获取到用户名后未存入自有数据库(如Firestore),导致后续登录无数据可读取。

修改后的代码:

Future signInWithApple() async {
  _hasError = false;
  notifyListeners();
  bool canAppleSignIn = await SignInWithApple.isAvailable();

  if (canAppleSignIn) {
    try {
      final credential = await SignInWithApple.getAppleIDCredential(
        scopes: [
          AppleIDAuthorizationScopes.email,
          AppleIDAuthorizationScopes.fullName,
        ],
      );

      final oauthCredential = OAuthProvider("apple.com").credential(
        idToken: credential.identityToken,
      );
      final UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
      final firebaseUser = userCredential.user;

      String? displayName;
      // 1. 优先从Apple返回值获取(仅首次登录有效)
      if (credential.givenName != null || credential.familyName != null) {
        displayName = [credential.givenName ?? '', credential.familyName ?? ''].join(' ').trim();
        if (firebaseUser != null && displayName.isNotEmpty) {
          await firebaseUser.updateDisplayName(displayName);
          // 手动刷新用户实例,获取最新displayName
          await firebaseUser.reload();
          final updatedUser = FirebaseAuth.instance.currentUser;
          displayName = updatedUser?.displayName;
        }
      } else {
        // 2. Apple未返回时,读取Firebase已存的displayName
        displayName = firebaseUser?.displayName;
        // 3. 若Firebase也无数据,从自有数据库读取之前保存的用户名
        // final firestoreDoc = await FirebaseFirestore.instance.collection('users').doc(firebaseUser?.uid).get();
        // displayName = firestoreDoc.data()?['name'];
      }

      // 邮箱更新逻辑(同样仅首次登录能获取Apple返回的email)
      if (firebaseUser != null && credential.email != null && (firebaseUser.email == null || firebaseUser.email!.isEmpty)) {
        await firebaseUser.updateEmail(credential.email!);
        await firebaseUser.reload();
      }

      // 使用处理后的displayName创建用户对象
      user = FireStoreUser(
        email: FirebaseAuth.instance.currentUser?.email,
        imageUrl: FirebaseAuth.instance.currentUser?.photoURL ?? "https://winaero.com/blog/wp-content/uploads/2017/12/User-icon-256-blue.png",
        name: displayName,
        provider: "APPLE",
        uid: firebaseUser!.uid,
      );

      // 首次登录时,将用户名持久化到自有数据库
      if (credential.givenName != null && firebaseUser != null) {
        await FirebaseFirestore.instance.collection('users').doc(firebaseUser.uid).set({
          'name': displayName,
          'email': FirebaseAuth.instance.currentUser?.email,
          'uid': firebaseUser.uid,
          'provider': "APPLE"
        }, SetOptions(merge: true));
      }

      await saveDataToSharedPreferences();
      notifyListeners();
    } on FirebaseAuthException catch (e) {
      switch (e.code) {
        case "account-exists-with-different-credential":
          _errorCode = "你已有账户,请使用对应登录方式";
          break;
        case "null":
          _errorCode = "登录时发生未知错误";
          break;
        default:
          _errorCode = e.toString();
      }
      _hasError = true;
      notifyListeners();
    }
  } else {
    _hasError = true;
    notifyListeners();
  }
}

关键注意事项

  • 首次授权必须持久化信息:拿到Apple返回的用户名和邮箱后,务必存入自有数据库,后续登录Apple不会再返回这些数据。
  • Firebase用户需手动刷新:调用updateDisplayName或updateEmail后,必须调用reload()并重新获取currentUser,才能拿到最新值。
  • 后续登录的降级逻辑:如果Apple未返回用户名,优先读取Firebase的displayName,再从自有数据库读取,避免出现空值。

内容的提问来源于stack exchange,提问作者Ahsan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 12:54:50