You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter Timer意外丢时/暂停问题及持久秒表实现咨询

针对你遇到的两个问题,我来分享下实际开发中经过验证的解决方案:

一、实现Flutter持久秒表的正确姿势

想要秒表在切换标签、甚至重启App后还能准确运行,绝对不要依赖Timer来累加时间——因为Timer本身受系统调度影响,很容易出现丢帧、暂停的情况。正确的思路是基于时间戳差值来计算时长,再配合持久化存储保存关键状态。

具体步骤:

  • 记录两个核心状态:已累计时长(秒表暂停时保存)和起始时间戳(秒表启动时记录当前系统时间),用SharedPreferences、Hive或者SQLite来持久化这些数据
  • 实时显示的时长 = 当前时间戳 - 起始时间戳 + 已累计时长
  • Stream.periodic或者Ticker来更新UI(比如每秒更新一次),但计算逻辑始终基于时间戳,而不是Timer的计数

举个简化的代码示例:

import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';

class StopwatchManager {
  late SharedPreferences _prefs;
  int _elapsedSeconds = 0;
  int? _startTimestamp;
  final StreamController<int> _timerController = StreamController.broadcast();
  Stream<int> get timerStream => _timerController.stream;

  Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
    // 从持久化存储恢复状态
    _elapsedSeconds = _prefs.getInt('elapsed_seconds') ?? 0;
    _startTimestamp = _prefs.getInt('start_timestamp');
    if (_startTimestamp != null) {
      // 如果之前是启动状态,继续计时
      _startTimerLoop();
    }
  }

  void _startTimerLoop() {
    Timer.periodic(const Duration(seconds: 1), (timer) {
      if (_startTimestamp == null) {
        timer.cancel();
        return;
      }
      final currentDuration = 
          (DateTime.now().millisecondsSinceEpoch ~/ 1000) - 
          (_startTimestamp! ~/ 1000) + 
          _elapsedSeconds;
      _timerController.add(currentDuration);
    });
  }

  void start() {
    if (_startTimestamp == null) {
      _startTimestamp = DateTime.now().millisecondsSinceEpoch;
      _prefs.setInt('start_timestamp', _startTimestamp!);
      _startTimerLoop();
    }
  }

  void pause() {
    if (_startTimestamp != null) {
      _elapsedSeconds = 
          (DateTime.now().millisecondsSinceEpoch ~/ 1000) - 
          (_startTimestamp! ~/ 1000) + 
          _elapsedSeconds;
      _prefs.setInt('elapsed_seconds', _elapsedSeconds);
      _startTimestamp = null;
      _prefs.remove('start_timestamp');
    }
  }

  void reset() {
    _elapsedSeconds = 0;
    _startTimestamp = null;
    _prefs.setInt('elapsed_seconds', 0);
    _prefs.remove('start_timestamp');
    _timerController.add(0);
  }

  void dispose() {
    _timerController.close();
  }
}
二、解决Timer未连接电脑时丢时的问题

你碰到的这个情况,本质是Android系统的省电机制在未连接电脑时限制了Flutter的Timer调度——连接电脑时,Android会自动禁用电池优化,所以Timer能正常运行;但断开电脑后,设备进入省电模式,系统会降低后台任务的优先级,甚至暂停Timer的执行。

解决方案分两种,推荐第一种:

1. 彻底放弃依赖Timer累加时间(最优解)

用上面提到的时间戳差值计算方法,不管Timer有没有被系统暂停,只要当前时间是准确的,计算出来的时长就不会出错。这是解决这类计时问题的根本方案,因为Timer本来就不是为精确计时设计的,它只是用来触发周期性任务。

2. 申请电池优化豁免(次选,仅用于必须依赖Timer的场景)

如果你的业务逻辑必须依赖Timer,可以引导用户将App加入电池优化白名单:

  • AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
  • 在Flutter代码中检查是否已获得豁免,如果没有则跳转系统设置页面:
import 'package:android_intent_plus/android_intent.dart';
import 'package:package_info_plus/package_info_plus.dart';

Future<void> requestIgnoreBatteryOptimizations() async {
  final packageInfo = await PackageInfo.fromPackageInfo();
  final intent = AndroidIntent(
    action: 'android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS',
    data: 'package:${packageInfo.packageName}',
  );
  await intent.launch();
}

注意:这种方法需要用户手动授权,而且部分厂商的定制系统可能会限制这个权限,所以只能作为补充方案,不能完全依赖。

内容的提问来源于stack exchange,提问作者Chris

火山引擎 最新活动