咨询:Flutter中用Shared Preferences实现计时器续计时方案
核心思路
要实现关闭应用再打开后计时器继续计时,需要存储两个关键数据:
totalElapsed:累计已经度过的毫秒数lastStartTime:上次开始计时的时间戳(如果计时器未处于运行状态,该值为null)
当应用重启时:
- 读取存储的
totalElapsed和lastStartTime - 如果
lastStartTime不为空,说明上次退出时计时器正在运行,需要计算从上次开始时间到当前时间的差值,加到totalElapsed上 - 基于更新后的
totalElapsed继续计时
步骤1:添加依赖
在pubspec.yaml中引入shared_preferences包:
dependencies: flutter: sdk: flutter shared_preferences: ^2.2.2 # 替换为最新版本即可
步骤2:完整实现代码
以下是可直接运行的示例页面:
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ResumableTimerPage extends StatefulWidget { const ResumableTimerPage({super.key}); @override State<ResumableTimerPage> createState() => _ResumableTimerPageState(); } class _ResumableTimerPageState extends State<ResumableTimerPage> { Timer? _timer; int _totalElapsed = 0; // 累计已过毫秒数 int? _lastStartTime; // 上次开始计时的时间戳(毫秒) bool _isRunning = false; @override void initState() { super.initState(); _loadSavedTimerData(); } @override void dispose() { _timer?.cancel(); _saveTimerData(); super.dispose(); } // 读取存储的计时器数据 Future<void> _loadSavedTimerData() async { final prefs = await SharedPreferences.getInstance(); setState(() { _totalElapsed = prefs.getInt('totalElapsed') ?? 0; _lastStartTime = prefs.getInt('lastStartTime'); }); // 上次退出时计时器在运行,直接恢复计时 if (_lastStartTime != null) { _resumeTimer(); } } // 保存计时器数据到SharedPreferences Future<void> _saveTimerData() async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt('totalElapsed', _totalElapsed); await prefs.setInt('lastStartTime', _lastStartTime ?? 0); } // 开始/恢复计时 void _startTimer() { if (_isRunning) return; setState(() { _lastStartTime = DateTime.now().millisecondsSinceEpoch; _isRunning = true; }); _timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { final currentTime = DateTime.now().millisecondsSinceEpoch; final elapsedSinceLastStart = currentTime - (_lastStartTime ?? currentTime); setState(() { _totalElapsed += elapsedSinceLastStart; _lastStartTime = currentTime; }); }); } // 暂停计时 void _pauseTimer() { if (!_isRunning) return; _timer?.cancel(); final currentTime = DateTime.now().millisecondsSinceEpoch; setState(() { _totalElapsed += currentTime - (_lastStartTime ?? currentTime); _lastStartTime = null; _isRunning = false; }); _saveTimerData(); } // 重置计时器 void _resetTimer() { _timer?.cancel(); setState(() { _totalElapsed = 0; _lastStartTime = null; _isRunning = false; }); _saveTimerData(); } // 恢复上次未完成的计时 void _resumeTimer() { final currentTime = DateTime.now().millisecondsSinceEpoch; setState(() { _totalElapsed += currentTime - (_lastStartTime ?? currentTime); _lastStartTime = currentTime; _isRunning = true; }); _timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { final now = DateTime.now().millisecondsSinceEpoch; setState(() { _totalElapsed += now - (_lastStartTime ?? now); _lastStartTime = now; }); }); } // 格式化毫秒为时分秒 String _formatTime(int milliseconds) { int seconds = (milliseconds ~/ 1000) % 60; int minutes = (milliseconds ~/ (1000 * 60)) % 60; int hours = (milliseconds ~/ (1000 * 60 * 60)) % 24; return '$hours:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('可恢复计时器')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( _formatTime(_totalElapsed), style: const TextStyle(fontSize: 40, fontWeight: FontWeight.bold), ), const SizedBox(height: 40), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: _isRunning ? _pauseTimer : _startTimer, child: Text(_isRunning ? '暂停' : '开始'), ), const SizedBox(width: 20), ElevatedButton( onPressed: _resetTimer, child: const Text('重置'), ), ], ), ], ), ), ); } }
关键细节说明
- 数据存储时机:在页面销毁(
dispose)和暂停计时时保存数据,确保退出应用时数据不会丢失 - 计时精度:用
millisecondsSinceEpoch获取毫秒级时间戳,保证计时准确性 - 内存泄漏避免:在
dispose方法中取消计时器,防止内存泄漏 - 异常处理:读取数据时用
??设置默认值,避免空指针问题
内容的提问来源于stack exchange,提问作者Jahan Zaib PJ
相关产品推荐
相关产品推荐

