Flutter应用锁屏后计时器停止问题求解
解决方案:Flutter锁屏后保持倒计时运行
方案一:基于时间戳的本地存储恢复
核心思路是记录倒计时的结束时间戳而非剩余秒数,解锁后通过当前时间与结束时间的差值计算剩余时长,完全规避后台运行限制。
实现步骤:
- 启动倒计时时,计算并保存「当前时间 + 总倒计时时长」的时间戳
- 监听应用生命周期,在进入后台(
AppLifecycleState.paused)时确保时间戳已保存 - 应用恢复前台(
AppLifecycleState.resumed)时,读取时间戳并计算剩余时长,重启计时器更新UI
代码示例:
import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; import 'dart:ui'; class CountdownHandler { Timer? _timer; int _remainingSeconds = 0; late int _endTimestamp; final _prefs = SharedPreferences.getInstance(); void start(int totalSeconds) async { _endTimestamp = DateTime.now().millisecondsSinceEpoch + (totalSeconds * 1000); await (await _prefs).setInt('countdown_end', _endTimestamp); _runTimer(); } void resume() async { final savedEnd = (await _prefs).getInt('countdown_end') ?? 0; if (savedEnd <= DateTime.now().millisecondsSinceEpoch) { _remainingSeconds = 0; return; } _endTimestamp = savedEnd; _remainingSeconds = (_endTimestamp - DateTime.now().millisecondsSinceEpoch) ~/ 1000; _runTimer(); } void _runTimer() { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { _remainingSeconds = (_endTimestamp - DateTime.now().millisecondsSinceEpoch) ~/ 1000; if (_remainingSeconds <= 0) { timer.cancel(); (await _prefs).remove('countdown_end'); } // 这里写更新UI的逻辑,比如通知StatefulWidget刷新 }); } } // 在主Widget中监听生命周期 @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { _countdownHandler.resume(); } }
方案二:借助原生后台能力(适合长时倒计时)
如果需要倒计时在后台持续运行且实时更新,需依赖Android/iOS的原生后台机制:
Android端:前台服务
- 申请
FOREGROUND_SERVICE权限,创建前台服务运行计时逻辑,通过MethodChannel与Flutter通信传递剩余时间 - 前台服务会显示持续通知,避免被系统后台杀死
iOS端:后台任务
使用
BackgroundTasks框架注册后台刷新任务,定期唤醒应用更新倒计时状态iOS对后台任务限制严格,仅适合短周期、非实时的倒计时场景
基础MethodChannel代码示例:
import 'package:flutter/services.dart'; class NativeTimer { static const _channel = MethodChannel('com.yourapp/timer'); static Future<void> startBackgroundTimer(int totalSeconds) async { await _channel.invokeMethod('start', {'seconds': totalSeconds}); } static Future<int> getRemaining() async { return await _channel.invokeMethod('getRemaining'); } }
方案三:独立Isolate运行计时
利用Flutter的Isolate脱离主UI线程的特性,让计时逻辑在独立Isolate中运行,降低被系统回收的概率:
- 代码示例:
import 'package:flutter_isolate/flutter_isolate.dart'; import 'dart:async'; void timerIsolate(SendPort sendPort) { int remaining = 120; // 示例总时长 Timer.periodic(const Duration(seconds: 1), (timer) { if (remaining <= 0) { timer.cancel(); sendPort.send(0); return; } remaining--; sendPort.send(remaining); }); } void startIsolateCountdown() async { final receivePort = ReceivePort(); await FlutterIsolate.spawn(timerIsolate, receivePort.sendPort); receivePort.listen((remaining) { // 接收Isolate传来的剩余时间,更新UI }); }
- 注意:该方案需结合方案一的时间戳备份做兜底,避免Isolate被系统回收导致计时丢失
内容的提问来源于stack exchange,提问作者Khaled Hossameldin
相关产品推荐
相关产品推荐

