Flutter应用部分Google OAuth认证用户数据未写入Firestore问题排查
问题排查与修复方案
1. 缺少错误捕获,写入失败无日志
checkUser()函数没有异常捕获逻辑,任何步骤(网络波动、FCM Token获取失败、权限问题)出错都会静默失败,无法定位原因。
修复:
给checkUser()添加try-catch块,打印错误信息:
Future<void> checkUser() async { try { // 原逻辑代码 } catch (e) { if (kDebugMode) { print('用户数据写入失败: $e'); } } }
2. 强制解包currentUser引发崩溃
虽然路由中判断了认证状态,但authStateChanges的流可能存在短暂的null状态(如网络延迟同步),强制解包FirebaseAuth.instance.currentUser!会直接崩溃,中断写入流程。
修复:
先判断用户是否为null,再执行后续逻辑:
final user = FirebaseAuth.instance.currentUser; if (user == null) return; // 避免崩溃,直接返回
3. 邮箱大小写不一致导致查询误判
Google认证返回的邮箱可能存在大小写差异,而Firestore的where查询区分大小写,会导致误判用户不存在/已存在,影响写入逻辑。
修复:
统一将邮箱转为小写存储和查询:
// 查询时转小写 var collection = FirebaseFirestore.instance .collection('users') .where('gmail', isEqualTo: user.email?.toLowerCase()); // 存储时也转小写 final userInformation = { // ...其他字段 'gmail': user.email?.toLowerCase(), };
4. FCM Token获取失败中断写入流程
FirebaseMessaging.instance.getToken()可能返回null或抛出异常,导致后续的set操作无法执行,整个写入流程失败。
修复:
单独捕获Token获取的异常,不影响核心用户数据写入:
String? firstToken; try { firstToken = await FirebaseMessaging.instance.getToken(); } catch (e) { if (kDebugMode) { print('FCM Token获取失败: $e'); } } // 写入时允许Token为null或空字符串 final userInformation = { // ...其他字段 'token': firstToken ?? '', };
5. 调用时机依赖页面,易被中断
checkUser()在HomeScreen的FutureBuilder中调用,若用户在Future完成前退出页面(如刚登录就返回),Future会被取消,写入流程中断。
修复:
将写入逻辑移至认证成功的回调中,确保认证完成后立即执行:
// 修改GoogleSignInProvider的googleLogin方法 Future googleLogin() async { try { // 原认证逻辑... await FirebaseAuth.instance.signInWithCredential(credential); // 认证成功后立即执行写入 await checkUser(); notifyListeners(); } catch (e) { if (kDebugMode) { print(e.toString()); } } }
6. 文档ID设计不合理,存在并发风险
当前用自动生成的文档ID,需要通过where查询判断用户是否存在,效率低且可能出现同一用户多次登录时的并发写入问题。
修复:
用Firebase用户的uid作为文档ID,直接通过文档ID判断是否存在,避免并发问题:
Future<void> checkUser() async { try { final user = FirebaseAuth.instance.currentUser; if (user == null) return; final userDoc = FirebaseFirestore.instance.collection('users').doc(user.uid); final docSnapshot = await userDoc.get(); if (!docSnapshot.exists) { String? firstToken; try { firstToken = await FirebaseMessaging.instance.getToken(); } catch (e) { if (kDebugMode) print('FCM Token获取失败: $e'); } final userInformation = { 'id': user.uid, 'name': user.displayName, 'gmail': user.email?.toLowerCase(), 'token': firstToken ?? '' }; await userDoc.set(userInformation); } else { // 用户已存在时的更新逻辑,比如更新Token String? newToken; try { newToken = await FirebaseMessaging.instance.getToken(); } catch (e) { if (kDebugMode) print('FCM Token获取失败: $e'); } await userDoc.update({'token': newToken ?? ''}); } } catch (e) { if (kDebugMode) { print('用户数据写入失败: $e'); } } }
内容的提问来源于stack exchange,提问作者Ini
相关产品推荐
相关产品推荐

