Flutter路由跳转页面时如何取消销毁Future.delayed定时器
问题原因
Future.delayed 本质是向Dart事件队列插入一个延时执行的任务,本身没有和Flutter页面生命周期做绑定。调用Navigator.pushReplacement跳转页面后,原页面State已经被销毁,但队列里的延时任务到预设时间依然会执行,此时调用已销毁State的setState方法就会抛出生命周期相关报错。
推荐解决方案
使用可手动取消的Timer替代Future.delayed,配合页面dispose生命周期在页面销毁时主动终止延时任务,同时增加mounted判断做兜底,完全避免页面销毁后执行无效逻辑。
- 第一步:引入Timer对应的依赖,在State类中声明定时器实例变量
- 第二步:替换原有
Future.delayed逻辑为Timer实现,每次启动新定时器前先取消上一个未执行完成的定时器,避免重复触发 - 第三步:重写
dispose生命周期方法,页面销毁时主动取消未执行完的定时器 - 第四步:所有触发
setState、弹SnackBar的UI操作前增加mounted判断,做双重兜底
完整修改代码
首先在文件头部引入异步依赖:
import 'dart:async';
在对应验证页面的State类中增加定时器变量,修改相关方法:
// State类内部声明定时器实例 Timer? _resendEmailTimer; bool canResendEmail = true; Future sendVerificationEmail() async { try{ final user = FirebaseAuth.instance.currentUser!; await user.sendEmailVerification(); // 取消上一轮可能残留的未执行定时器 _resendEmailTimer?.cancel(); setState(() => canResendEmail = false); // 替换原Future.delayed逻辑 _resendEmailTimer = Timer(const Duration(seconds: 5), () { // 页面未挂载时不执行UI操作 if (mounted) { setState(() => canResendEmail = true); } }); }catch (e) { // 页面销毁后不再弹出提示 if (mounted) { Utils.showSnackBar(e.toString()); } } } // 重写dispose方法 @override void dispose() { // 页面销毁时立刻终止延时任务 _resendEmailTimer?.cancel(); super.dispose(); }
其他兜底方案(不推荐单独使用)
如果不想替换Future.delayed,可以在每个setState、UI操作前强制判断mounted属性,页面已销毁时直接return跳过后续逻辑,也能避免报错,但这种方式无法真正取消队列里的延时任务,会留无用的事件在队列里,性能不如Timer取消的方案干净。
示例修改片段:
setState(() => canResendEmail = false); await Future.delayed(const Duration(seconds: 5)); // 延时结束先判断页面是否存在 if (!mounted) return; setState(() => canResendEmail = true);
注意:原有的SignOut跳转逻辑不需要额外修改,页面被替换时会自动触发
dispose生命周期,自动执行定时器取消逻辑。
内容的提问来源于stack exchange,提问作者taha khamis
相关产品推荐
相关产品推荐

