Flutter中circular_countdown_timer后台锁屏时无法自动重启问题
解决Flutter中circular_countdown_timer后台/锁屏时无法重启的问题
问题根源:当应用进入后台或锁屏状态,Flutter引擎会暂停UI线程的定时器,导致circular_countdown_timer的onComplete回调无法触发,自然无法执行重启逻辑。以下是几种可行的解决方案:
方案一:使用前台服务保持后台活跃
通过前台服务让应用在后台持续运行,确保计时器的回调能正常触发。推荐使用flutter_foreground_task插件:
步骤1:添加依赖
在pubspec.yaml中加入:
dependencies: flutter_foreground_task: ^6.1.0 circular_countdown_timer: ^0.2.2
步骤2:配置平台权限
- Android:在
android/app/src/main/AndroidManifest.xml中添加权限和服务声明:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application ...> <service android:name="com.pravera.flutter_foreground_task.service.ForegroundService" android:foregroundServiceType="dataSync|location|mediaPlayback" /> </application>
- iOS:在
ios/Runner/Info.plist中添加后台模式:
<key>UIBackgroundModes</key> <array> <string>fetch</string> <string>processing</string> </array>
步骤3:实现代码
class CountdownPage extends StatefulWidget { const CountdownPage({super.key}); @override State<CountdownPage> createState() => _CountdownPageState(); } class _CountdownPageState extends State<CountdownPage> { final int _duration = 10; // 倒计时时长(秒) final CountDownController _controller = CountDownController(); @override void initState() { super.initState(); _startForegroundService(); } // 启动前台服务 void _startForegroundService() async { await FlutterForegroundTask.startService( notificationTitle: '倒计时运行中', notificationText: '后台保持计时器活跃', notificationIcon: const NotificationIcon( resType: ResourceType.mipmap, resPrefix: 'ic_launcher', ), ); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: CircularCountDownTimer( duration: _duration, controller: _controller, onComplete: () { // 前台/后台均能触发重启 _controller.restart(); }, width: MediaQuery.of(context).size.width / 2, height: MediaQuery.of(context).size.height / 2, ringColor: Colors.grey[300]!, fillColor: Colors.blueAccent, strokeWidth: 20.0, strokeCap: StrokeCap.round, textStyle: const TextStyle( fontSize: 33.0, color: Colors.black, fontWeight: FontWeight.bold, ), autoStart: true, ), ), ); } @override void dispose() { // 页面销毁时停止前台服务 FlutterForegroundTask.stopService(); super.dispose(); } }
方案二:使用Isolate实现独立后台计时器
创建独立于UI线程的Isolate运行倒计时逻辑,避免UI线程暂停影响计时器:
步骤1:添加依赖
dependencies: flutter_isolate: ^2.0.4 circular_countdown_timer: ^0.2.2
步骤2:实现代码
class CountdownPage extends StatefulWidget { const CountdownPage({super.key}); @override State<CountdownPage> createState() => _CountdownPageState(); } class _CountdownPageState extends State<CountdownPage> { final int _duration = 10; final CountDownController _controller = CountDownController(); FlutterIsolate? _backgroundIsolate; late ReceivePort _receivePort; @override void initState() { super.initState(); _initBackgroundIsolate(); } // 初始化后台Isolate void _initBackgroundIsolate() async { _receivePort = ReceivePort(); _backgroundIsolate = await FlutterIsolate.spawn( _runBackgroundCountdown, _receivePort.sendPort, ); // 监听Isolate的消息,触发UI计时器重启 _receivePort.listen((message) { if (message == 'restart' && mounted) { _controller.restart(); // 重启后台计时器 _backgroundIsolate?.kill(); _initBackgroundIsolate(); } }); } // 后台Isolate的倒计时逻辑 static void _runBackgroundCountdown(SendPort sendPort) async { await Future.delayed(const Duration(seconds: 10)); sendPort.send('restart'); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: CircularCountDownTimer( duration: _duration, controller: _controller, onComplete: () { // 前台时同步重启 _controller.restart(); }, width: MediaQuery.of(context).size.width / 2, height: MediaQuery.of(context).size.height / 2, ringColor: Colors.grey[300]!, fillColor: Colors.blueAccent, strokeWidth: 20.0, strokeCap: StrokeCap.round, textStyle: const TextStyle( fontSize: 33.0, color: Colors.black, fontWeight: FontWeight.bold, ), autoStart: true, ), ), ); } @override void dispose() { _backgroundIsolate?.kill(); _receivePort.close(); super.dispose(); } }
方案三:使用Workmanager(适合长周期倒计时)
如果倒计时时长≥15分钟(Android平台Workmanager周期任务最小间隔限制),可以用Workmanager注册周期任务,结合本地存储同步状态:
步骤1:添加依赖
dependencies: workmanager: ^0.5.1 shared_preferences: ^2.2.2 circular_countdown_timer: ^0.2.2
步骤2:实现代码
// 后台任务回调 void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { final prefs = await SharedPreferences.getInstance(); // 标记需要重启计时器 await prefs.setBool('shouldRestart', true); return Future.value(true); }); } void main() { WidgetsFlutterBinding.ensureInitialized(); Workmanager().initialize( callbackDispatcher, isInDebugMode: true, ); runApp(const MyApp()); } class CountdownPage extends StatefulWidget { const CountdownPage({super.key}); @override State<CountdownPage> createState() => _CountdownPageState(); } class _CountdownPageState extends State<CountdownPage> { final int _duration = 900; // 15分钟(符合Workmanager最小间隔) final CountDownController _controller = CountDownController(); late SharedPreferences _prefs; @override void initState() { super.initState(); _initPrefs(); _registerBackgroundTask(); } void _initPrefs() async { _prefs = await SharedPreferences.getInstance(); // 应用回到前台时检查是否需要重启 if (_prefs.getBool('shouldRestart') ?? false) { _controller.restart(); await _prefs.setBool('shouldRestart', false); } } // 注册周期后台任务 void _registerBackgroundTask() { Workmanager().registerPeriodicTask( 'countdown_restart', 'countdownTask', frequency: Duration(seconds: _duration), initialDelay: Duration(seconds: _duration), ); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: CircularCountDownTimer( duration: _duration, controller: _controller, onComplete: () { _controller.restart(); _prefs.setBool('shouldRestart', false); }, width: MediaQuery.of(context).size.width / 2, height: MediaQuery.of(context).size.height / 2, ringColor: Colors.grey[300]!, fillColor: Colors.blueAccent, strokeWidth: 20.0, strokeCap: StrokeCap.round, textStyle: const TextStyle( fontSize: 33.0, color: Colors.black, fontWeight: FontWeight.bold, ), textFormat: CountdownTextFormat.MM_SS, autoStart: true, ), ), ); } @override void dispose() { Workmanager().cancelAll(); super.dispose(); } }
注意事项
- iOS对后台运行限制严格,前台服务和Isolate可能无法长时间保持活跃,需根据业务场景选择方案;
- 前台服务会在状态栏显示通知,用户可感知,适合需要持续后台运行的场景;
- Isolate相对轻便,但部分安卓机型可能在后台被系统回收,可搭配
wakelock插件保持设备唤醒。
内容的提问来源于stack exchange,提问作者Qurat ul ain
相关产品推荐
相关产品推荐

