Flutter集成Firebase谷歌登录:通过idToken自动登录触发框架错误
解决Flutter中Firebase谷歌自动登录的框架错误问题
我帮你排查下代码里的几个关键问题,这些应该就是触发框架错误的核心原因:
1. 条件判断逻辑完全错误
你写的if (idToken != null || idToken != "")逻辑永远为真——不管idToken是null还是空字符串,总有一个条件会满足。这会导致你拿着无效的空令牌去调用signInWithCredential,直接触发框架错误。
正确的判断应该是同时满足非空且非空字符串,用&&连接,还可以用Dart更简洁的写法:
if (idToken?.isNotEmpty == true && accessToken?.isNotEmpty == true)
2. 没必要的setState引发的生命周期问题
在await SharedPreferences之后调用setState完全是多余的——你只是在启动页做登录校验,不需要更新UI状态,反而可能因为此时Widget还没完全初始化完成,触发框架的生命周期异常。直接赋值即可,去掉setState包裹。
3. 未处理令牌过期的异常
谷歌的idToken和accessToken是有有效期的(通常1小时),如果用户退出App超过1小时,令牌就会失效,此时用旧令牌登录会抛出Firebase认证异常。你需要添加异常捕获,避免崩溃。
修改后的完整代码
Future<void> loginGoogle() async { SharedPreferences pref = await SharedPreferences.getInstance(); // 直接赋值,去掉不必要的setState final idToken = pref.getString("idToken"); final accessToken = pref.getString("accessToken"); final loginStatus = pref.getInt("loginStatus"); print(idToken); // 修正条件判断逻辑,同时校验两个令牌 if (idToken?.isNotEmpty == true && accessToken?.isNotEmpty == true) { try { AuthCredential credential = GoogleAuthProvider.getCredential( idToken: idToken!, accessToken: accessToken!); AuthResult result = await auth.signInWithCredential(credential); final user = result.user; if (user != null) { if (!user.isEmailVerified) { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => Login()), (route) => false); } else { print("berhasil login"); Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => Home()), (route) => false); } } else { // 用户为空,跳转到登录页 Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => Login()), (route) => false); } } catch (e) { // 捕获令牌过期或其他认证异常,直接跳转到登录页 print("自动登录失败: $e"); Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => Login()), (route) => false); } } else { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => Login()), (route) => false); } }
额外建议:不要自己持久化令牌
其实Firebase Auth本身已经帮你处理了用户会话的持久化,你完全不需要手动存idToken和accessToken。更优雅的自动登录方式是直接监听用户状态:
@override void initState() { super.initState(); auth.authStateChanges().listen((User? user) { if (user != null) { // 用户已登录,判断邮箱是否验证 if (user.isEmailVerified) { Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => Home())); } else { Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => Login())); } } else { // 用户未登录,跳转到登录页 Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => Login())); } }); }
这种方式既避免了手动存令牌的各种问题,又能实时响应用户状态变化。
内容的提问来源于stack exchange,提问作者Richard Kyle
相关产品推荐
相关产品推荐

