Flutter Firebase登录触发FirebaseAuthException但仍登录成功,且登录函数被重复调用问题排查
分析与解决方案:FirebaseAuth重复调用导致空参数异常
从你的描述和代码来看,核心问题是**login函数被意外调用了两次**:一次传入了正确的凭证(触发HERE1),另一次传入空字符串(触发HERE2的异常)。异步操作的并发特性导致了HERE1和HERE2的打印顺序不稳定,你临时加的空判断只是治标,我们需要找到重复调用的根源。
可能的触发原因及排查方向
1. checkLocalBiometrics被重复执行
checkLocalBiometrics的调用时机可能存在重复触发的情况:
- 如果它是在
initState中调用,当页面因路由跳转、屏幕旋转或状态重建时,initState会再次执行,导致函数重复调用。 - 如果是通过GetX的状态监听或Controller初始化触发,也可能因为状态变化多次执行。
解决方法:给函数加一个执行标记,避免重复运行:
bool _hasCheckedBiometrics = false; void checkLocalBiometrics() async { // 已经执行过就直接返回 if (_hasCheckedBiometrics) return; _hasCheckedBiometrics = true; if (await credentialsSaved) { // 后续原有逻辑不变 var localAuth = LocalAuthentication(); bool canCheckBiometrics = await localAuth.canCheckBiometrics; if (canCheckBiometrics) { var didAuthenticate = await localAuth.authenticate( localizedReason: "-----------------", biometricOnly: true, stickyAuth: true, ); if (didAuthenticate) { var secureStorage = FlutterSecureStorage(); var storedEmail = await secureStorage.read(key: "email"); var storedPassword = await secureStorage.read(key: "password"); login(email: storedEmail!, password: storedPassword!); } } } }
2. stickyAuth: true的特性触发重复验证
你设置了stickyAuth: true,这个参数的作用是:当App被中断(比如来电、切换到后台)后回到前台时,会重新触发生物识别验证。如果用户在验证过程中切换了后台,可能会导致didAuthenticate被多次返回true,进而重复调用login。
解决方法:
- 先尝试将
stickyAuth改为false,看是否还会出现重复调用; - 或者在验证成功后加一个标记,避免重复执行登录逻辑:
bool _hasAuthenticated = false; void checkLocalBiometrics() async { if (await credentialsSaved) { var localAuth = LocalAuthentication(); bool canCheckBiometrics = await localAuth.canCheckBiometrics; if (canCheckBiometrics) { var didAuthenticate = await localAuth.authenticate( localizedReason: "-----------------", biometricOnly: true, stickyAuth: true, ); if (didAuthenticate && !_hasAuthenticated) { _hasAuthenticated = true; // 标记已完成验证 var secureStorage = FlutterSecureStorage(); var storedEmail = await secureStorage.read(key: "email"); var storedPassword = await secureStorage.read(key: "password"); login(email: storedEmail!, password: storedPassword!); } } } }
3. 异步竞态与非空断言的潜在风险
虽然credentialsSaved校验了邮箱非空,但你用了storedEmail!和storedPassword!的非空断言,极端情况下(比如存储的密码被意外清空)会导致空参数调用。建议在调用login前增加双重校验:
if (storedEmail != null && storedPassword != null) { login(email: storedEmail, password: storedPassword); } else { print("存储的凭证不完整,请重新登录"); // 这里可以跳转到登录页面,清空无效存储 await secureStorage.deleteAll(); }
关于打印顺序混乱的解释
因为两次login调用是并发执行的异步操作:第一次正常登录的signInWithEmailAndPassword和第二次空参数的请求同时在后台处理,它们的完成顺序是不确定的,所以HERE1和HERE2的打印顺序会随机变化。
额外排查建议
在checkLocalBiometrics和login函数开头增加日志,明确调用次数和参数:
void checkLocalBiometrics() async { print("🔍 checkLocalBiometrics 被调用"); // 原有逻辑... } void login({required String email, required String password}) async { print("🔑 login 被调用:email=$email, password=${password.isEmpty ? '空' : '非空'}"); // 原有逻辑... }
通过日志可以直观看到是不是真的被调用了两次,以及每次的参数情况,帮助定位问题。
内容的提问来源于stack exchange,提问作者Phake
相关产品推荐
相关产品推荐

