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

Flutter中如何将计时器保存至本地内存?

实现Flutter后台持续计时并重启恢复的方案

你之前用SharedPreferences失败的核心原因是直接存储剩余时间——应用退到后台后,计时器会暂停,存储的剩余时间不会随系统时间自动减少,重启后显示的就不是真实剩余时长。正确的思路是存储计时器的结束时间戳,利用系统时间的持续流逝来计算剩余时间。

核心逻辑

  1. 点击开始计时时,计算出「当前时间 + 总计时时长」的结束时间戳(毫秒级),将这个值存在SharedPreferences里。
  2. 重启应用时,读取存储的结束时间戳,和当前系统时间对比:
    • 如果当前时间已超过结束时间,说明计时完成,重置状态。
    • 如果未超过,用「结束时间 - 当前时间」算出真实剩余时长,再启动计时器刷新UI。

代码实现示例

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
import 'dart:math';

class PersistentTimerPage extends StatefulWidget {
  const PersistentTimerPage({super.key});

  @override
  State<PersistentTimerPage> createState() => _PersistentTimerPageState();
}

class _PersistentTimerPageState extends State<PersistentTimerPage> {
  late SharedPreferences _prefs;
  int? _endTimeMillis; // 存储的结束时间戳(毫秒)
  int _remainingSeconds = 0;
  Timer? _refreshTimer;

  @override
  void initState() {
    super.initState();
    _initPrefsAndRestoreTimer();
  }

  // 初始化SharedPreferences并恢复计时状态
  Future<void> _initPrefsAndRestoreTimer() async {
    _prefs = await SharedPreferences.getInstance();
    _endTimeMillis = _prefs.getInt('timer_end_timestamp');
    _calculateRemainingTime();
    _startRefreshTimer();
  }

  // 计算并更新剩余时间
  void _calculateRemainingTime() {
    if (_endTimeMillis == null) {
      setState(() => _remainingSeconds = 0);
      return;
    }

    final now = DateTime.now().millisecondsSinceEpoch;
    final remainingMillis = _endTimeMillis! - now;

    if (remainingMillis <= 0) {
      // 计时已结束,清除存储的时间戳
      setState(() => _remainingSeconds = 0);
      _prefs.remove('timer_end_timestamp');
      _endTimeMillis = null;
      _refreshTimer?.cancel();
    } else {
      setState(() => _remainingSeconds = (remainingMillis / 1000).ceil());
    }
  }

  // 启动每秒刷新UI的计时器
  void _startRefreshTimer() {
    _refreshTimer?.cancel();
    if (_endTimeMillis != null && _remainingSeconds > 0) {
      _refreshTimer = Timer.periodic(const Duration(seconds: 1), (_) {
        _calculateRemainingTime();
      });
    }
  }

  // 触发开始计时(示例为60秒计时)
  void _triggerTimer() {
    const totalCountdownSeconds = 60;
    final now = DateTime.now().millisecondsSinceEpoch;
    _endTimeMillis = now + totalCountdownSeconds * 1000;
    
    // 存储结束时间戳
    _prefs.setInt('timer_end_timestamp', _endTimeMillis!);
    _calculateRemainingTime();
    _startRefreshTimer();
  }

  @override
  void dispose() {
    _refreshTimer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('后台持续计时器')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '剩余时间: $_remainingSeconds 秒',
              style: const TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _remainingSeconds > 0 ? null : _triggerTimer,
              child: const Text('开始60秒计时'),
            ),
          ],
        ),
      ),
    );
  }
}

补充说明

  • 这个方案不依赖后台服务,完全靠系统时间和存储的结束时间戳计算,即使应用被杀死,重启后也能立即得到真实剩余时长。
  • 如果需要后台提醒(比如计时结束推送通知),可以搭配flutter_local_notifications库,在_calculateRemainingTime中判断剩余时间为0时发送通知即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:43:31