Flutter authStateChanges监听报widget卸载无context异常解决方法
报错根因
FirebaseAuth.instance.authStateChanges().listen 注册的是持续生效的流订阅,当承载这段逻辑的Widget被销毁(unmounted)后,订阅不会自动取消,如果此时认证状态变化触发回调,回调内访问已经失效的context执行导航操作,就会抛出该异常。
你注释掉的mounted判断是必要修复手段,配合订阅取消逻辑,完全不会影响原有pushAndRemoveUntil跳转、触发页面dispose的业务需求。
修复方案
- 存储流订阅实例,在Widget的
dispose生命周期中主动取消订阅,从根源避免Widget销毁后回调被触发 - 所有访问
context、执行导航操作的逻辑之前,增加mounted判断,只有当前Widget处于活跃挂载状态时才执行后续逻辑 - 修正代码中重复声明
user变量的问题:监听回调已经返回最新的user对象,不需要额外调用FirebaseAuth.instance.currentUser取值,避免状态不一致 - 不要把监听注册逻辑放在
build方法内,build会随页面刷新反复执行,会生成多个重复订阅,导致逻辑重复触发、内存泄漏,监听注册逻辑应该放在initState生命周期中执行
完整修复代码
在对应State类中声明订阅变量,重写initState和dispose方法:
class _YourPageState extends State<YourPage> { // 声明认证状态监听的订阅实例 late StreamSubscription<User?> _authStateSub; UserModel loggedInUser = UserModel(); @override void initState() { super.initState(); // 在initState中注册监听,避免重复创建 _authStateSub = FirebaseAuth.instance.authStateChanges().listen((User? user) { // 先判断当前Widget是否还处于挂载状态,未挂载直接终止逻辑 if (!mounted) return; if (user == null) { print('------------------------------------------------------User is currently signed out!'); print(user); Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (BuildContext context) => const LoginScreen()), (Route<dynamic> route) => false ); } else { print(user); print('--------------------------------------------------User is signed in!'); Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (context) => const loadData()), (Route<dynamic> route) => false ); } }); } @override void dispose() { // 页面销毁时主动取消订阅,释放资源 _authStateSub.cancel(); super.dispose(); } // 原有build逻辑保持不变 @override Widget build(BuildContext context) { // ... 你的原有页面构建代码 } }
说明
原有
pushAndRemoveUntil跳转逻辑完全保留,跳转时会清空全部历史路由栈,被移除的页面会正常触发dispose生命周期,符合业务要求。mounted判断只会拦截Widget已经销毁后的无效导航操作,正常场景下的登录、登出跳转逻辑不会受任何影响。
内容的提问来源于stack exchange,提问作者CodeConfusion
相关产品推荐
相关产品推荐

