Flutter中Timer.periodic触发15分钟定时函数失效问题排查
问题分析与修复方案
核心问题
你的触发逻辑if ((timesupSeconds % (15 * 60)) == 0)有两个关键缺陷:
- Timer执行精度偏差:
Timer.periodic的1秒间隔不是绝对精准的,系统调度延迟可能让回调在整秒之后才执行(比如第900秒的100ms处),这时timesupSeconds已经变成901,直接错过等于0的瞬间。 - 无去重机制:即使刚好命中整秒,每秒执行的Timer可能在同一秒内重复触发逻辑,同时错过触发点的概率极高。
修复代码示例
方案1:基于周期区间判断(兼容原有逻辑)
void clock() { int? lastTriggeredSecond; // 记录上一次触发的秒数,防止重复执行 Timer.periodic(const Duration(seconds: 1), (clock) { var timesUp = DateTime.now().millisecondsSinceEpoch; var timesupSeconds = (timesUp / 1000).floor(); final fifteenMinutes = 15 * 60; // 计算当前在15分钟周期内的剩余秒数 final cycleSeconds = timesupSeconds % fifteenMinutes; var minutes = (cycleSeconds / 60).floor(); var seconds = cycleSeconds % 60; // 仅当刚进入新的15分钟周期且未触发过时执行逻辑 if (cycleSeconds == 0 && lastTriggeredSecond != timesupSeconds) { print('it is 15 minutes'); lastTriggeredSecond = timesupSeconds; } setState(() { time = '$minutes m: $seconds'; }); }); }
方案2:一次性Timer精准触发(更可靠)
直接计算距离下一个15分钟节点的时间,到点后执行逻辑并重新调度,完全规避周期Timer的精度问题:
void startClock() { // 初始化计时显示 updateTimerDisplay(); // 调度第一次15分钟触发 scheduleNext15MinuteTrigger(); } void updateTimerDisplay() { var timesUp = DateTime.now().millisecondsSinceEpoch; var timesupSeconds = (timesUp / 1000).floor(); final fifteenMinutes = 15 * 60; final cycleSeconds = timesupSeconds % fifteenMinutes; var minutes = (cycleSeconds / 60).floor(); var seconds = cycleSeconds % 60; setState(() { time = '$minutes m: $seconds'; }); // 每秒更新一次计时 Timer(const Duration(seconds: 1), updateTimerDisplay); } void scheduleNext15MinuteTrigger() { var now = DateTime.now(); // 计算下一个15分钟整的时间点(比如当前是12:07,下一个是12:15) var nextTrigger = DateTime(now.year, now.month, now.day, now.hour, (now.minute ~/ 15 + 1) * 15, 0); var timeToTrigger = nextTrigger.difference(now); Timer(timeToTrigger, () { print('it is 15 minutes'); // 触发后继续调度下一次 scheduleNext15MinuteTrigger(); }); }
额外说明
- 方案1适合保留原有周期Timer的逻辑,通过区间判断和去重解决触发失效问题;
- 方案2的精准度更高,分开了计时显示和触发逻辑,代码结构更清晰,适合对触发时间要求严格的场景。
内容的提问来源于stack exchange,提问作者Deenan Djigbenou
相关产品推荐
相关产品推荐

