Flutter使用CustomTimer时切换页面后计时器重启问题如何修复
问题原因
- 路由逻辑错误:左上角返回按钮使用
Navigator.push新建首页实例,而非返回上一级页面,每次重新进入计时器页面都会生成全新的页面实例,原有计时状态全部丢失。 - 代码逻辑错误:启动按钮的点击回调中直接调用
animationController.dispose(),刚启动就销毁动画控制器,导致动画和计时关联逻辑异常。 - 页面无状态保活:如果是底部导航多页面切换场景,默认切换页面时会销毁不可见的页面实例,计时状态随页面销毁被清空。
- 无持久化存储:计时状态仅保存在页面内存中,没有本地持久化,就算不切页面,APP被杀后台、清缓存后计时也会重置。
- 多余的
MaterialApp嵌套:计时器页面内重复套了MaterialApp,导致路由上下文混乱,进一步加剧页面实例重建问题。
修复方案
1. 修复明显代码错误
首先删除启动按钮中错误的销毁逻辑,统一在页面生命周期中销毁控制器避免内存泄漏:
// 启动按钮修改为 onPressed: () { timerController.start(); animationController.forward(); // 删除这里的animationController.dispose() }, // 给State新增dispose生命周期 @override void dispose() { timerController.dispose(); animationController.dispose(); super.dispose(); }
2. 修复路由逻辑和多余嵌套
去掉页面内多余的MaterialApp,同时把返回按钮的push逻辑改成pop,避免新建页面实例:
// build方法直接返回Scaffold,删除外层的MaterialApp @override Widget build(BuildContext context) { super.build(context); return Scaffold( // 原来的Scaffold代码,背景色可以写到这里的backgroundColor backgroundColor: const Color.fromRGBO(73, 97, 222, 100), body: SingleChildScrollView( // 原来的body内容不变 ), ); } // 返回按钮逻辑修改为 onPressed: () { Navigator.pop(context); // 直接返回上一级页面,保留当前计时器页面的状态 },
3. 底部导航切换场景保活页面
如果你的计时器是底部导航的其中一个tab页,需要给State混入保活Mixin避免切换时被销毁:
class _TimerState extends State<Timer> with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { // 重写保活配置 @override bool get wantKeepAlive => true; // 剩下的initState、build等代码不变,注意build方法要调用super.build(context) }
4. 实现本地持久化,杀APP也能继续计时
引入官方的shared_preferences依赖,存储计时启动的时间戳,每次进入页面自动计算剩余时间:
// 先定义全局常量 final totalTimerDuration = const Duration(days: 14); late CustomTimerController timerController; // initState中新增初始化逻辑 @override void initState() { super.initState(); _initTimerState(); // 原来的animationController初始化逻辑不变 } Future<void> _initTimerState() async { final prefs = await SharedPreferences.getInstance(); final startTimestamp = prefs.getInt('isoman_start_time'); if (startTimestamp != null) { // 已经启动过计时,计算剩余时间 final passedTime = DateTime.now().difference(DateTime.fromMillisecondsSinceEpoch(startTimestamp)); final remainingTime = totalTimerDuration - passedTime; timerController = CustomTimerController( initialRemaining: remainingTime.isNegative ? Duration.zero : remainingTime ); // 未结束则自动继续计时 if (!remainingTime.isNegative) { timerController.start(); animationController.forward(); } } else { // 未启动过,初始化默认时长 timerController = CustomTimerController(initialRemaining: totalTimerDuration); } setState(() {}); } // 启动按钮新增存储启动时间的逻辑 onPressed: () async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt('isoman_start_time', DateTime.now().millisecondsSinceEpoch); timerController.start(); animationController.forward(); }, // CustomTimer的from参数改成动态剩余时间,不要写死14天 CustomTimer( from: timerController.remaining, to: const Duration(seconds: 0), controller: timerController, // 剩下的builder逻辑不变 )
内容的提问来源于stack exchange,提问作者Lenza
相关产品推荐
相关产品推荐

