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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 15:35:21