You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter使用CustomTimer时切换页面后计时器重启问题如何修复

问题原因

  1. 路由逻辑错误:左上角返回按钮使用Navigator.push新建首页实例,而非返回上一级页面,每次重新进入计时器页面都会生成全新的页面实例,原有计时状态全部丢失。
  2. 代码逻辑错误:启动按钮的点击回调中直接调用animationController.dispose(),刚启动就销毁动画控制器,导致动画和计时关联逻辑异常。
  3. 页面无状态保活:如果是底部导航多页面切换场景,默认切换页面时会销毁不可见的页面实例,计时状态随页面销毁被清空。
  4. 无持久化存储:计时状态仅保存在页面内存中,没有本地持久化,就算不切页面,APP被杀后台、清缓存后计时也会重置。
  5. 多余的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 04:54:02