Flutter使用pushAndRemoveUntil退出后重入触发dispose异常如何处理
异常原因
这个报错是因为异步请求发起后,页面已经被pushAndRemoveUntil销毁(unmounted),但请求回调返回时仍尝试调用已销毁页面State的context执行路由跳转,此时State对应的组件已经不在组件树中,context已经失效。
修复方案
按实现成本和适配范围从易到难排序:
方案1:异步回调前增加mounted判断(官方推荐快速修复方案)
所有await异步操作结束后,需要用到context、调用setState等依赖当前State的逻辑前,先判断State的mounted属性,只有State仍挂载时才执行后续逻辑:
void fetchData() async { final res = await yourApiRequest(); // 新增mounted判断,页面已销毁时直接终止后续逻辑 if (!mounted) return; if (res.code == 401) { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (ctx) => const LoginPage()), (route) => false, ); } }
方案2:页面销毁时取消未完成的网络请求
如果使用Dio作为网络库,可以通过CancelToken在页面销毁时主动取消所有未完成的请求,从根源避免回调执行:
class _YourPageState extends State<YourPage> { final CancelToken _cancelToken = CancelToken(); @override void dispose() { // 页面销毁时取消所有绑定该token的请求 _cancelToken.cancel("page disposed"); super.dispose(); } void fetchData() async { try { final res = await dio.get( "/api/path", cancelToken: _cancelToken, // 绑定cancelToken到请求 ); if (!mounted) return; if (res.code == 401) { // 跳转逻辑 } } on DioError catch (e) { // 忽略请求取消类型的异常 if (CancelToken.isCancel(e)) return; // 其他异常处理 } } }
方案3:全局统一处理401跳转(最优解,一劳永逸)
无需在每个页面单独处理401逻辑,通过全局导航Key直接执行跳转,完全不依赖页面级context,从根源避免context失效问题:
- 首先在
main.dart中定义全局导航Key并绑定到MaterialApp:
// 全局导航Key,可直接获取顶层NavigatorState final GlobalKey<NavigatorState> globalNavigatorKey = GlobalKey<NavigatorState>(); void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: globalNavigatorKey, // 绑定全局导航Key home: const HomePage(), ); } }
- 在网络库的全局拦截器中统一处理401跳转:
dio.interceptors.add(InterceptorsWrapper( onResponse: (response, handler) { if (response.statusCode == 401) { // 直接用全局导航Key跳转,无需页面context globalNavigatorKey.currentState?.pushAndRemoveUntil( MaterialPageRoute(builder: (ctx) => const LoginPage()), (route) => false, ); } return handler.next(response); }, ));
注意事项
- 异步操作后的
setState调用前也必须加mounted判断,避免同类报错 - 未使用Dio的场景可以手动在State中维护
bool _isDisposed = false标记,dispose时设为true,回调中判断该标记即可达到和mounted相同的效果 - 全局导航Key也可用于全局弹窗、跨页面路由跳转等场景,无需依赖页面上下文
内容的提问来源于stack exchange,提问作者andy
相关产品推荐
相关产品推荐

