Flutter三方库适配OpenHarmony【countdown_timer】倒计时器项目完整实战

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

countdown_timer 是一个基于 Flutter 的倒计时器项目,核心代码位于 lib/main.dart。项目支持 1、5、10、15、30、60 分钟预设,也支持通过时、分、秒三个步进器手动设置时间。点击 Start 后,页面使用 Future.doWhileFuture.delayed 每秒递减剩余时间;运行中可以 Pause 暂停,也可以 Reset 重置;倒计时结束后通过 AlertDialog 显示 Time's Up! 提示。

这个项目适合讲解 Flutter 工具类应用在 OpenHarmony 上的适配过程。它覆盖了 状态机设计时分秒换算异步计时循环生命周期保护条件渲染按钮显隐结束弹窗Material 3 组件渲染

在这里插入图片描述

图片说明:本文围绕 Flutter 状态管理、异步计时和 OpenHarmony 承载工程展开,所有关键代码均来自 countdown_timer 的真实源码。

倒计时器的重点不是简单地把数字减到 0,而是让设置、开始、暂停、完成和重置每个状态都明确、可控、可恢复。

一、项目背景与目标

1.1 项目定位

countdown_timer 是一个轻量倒计时工具。用户可以通过预设按钮快速设置分钟数,也可以通过时、分、秒步进器精确调整时间。设置完成后,页面展示圆形倒计时读数,并根据运行状态显示 Start、Pause、Reset 按钮。

当前项目真实支持的功能包括:

  • 默认显示 00:00:00
  • 支持 1 分钟预设。
  • 支持 5 分钟预设。
  • 支持 10 分钟预设。
  • 支持 15 分钟预设。
  • 支持 30 分钟预设。
  • 支持 60 分钟预设。
  • 支持小时步进,范围 0 到 23。
  • 支持分钟步进,范围 0 到 59。
  • 支持秒步进,范围 0 到 59。
  • 使用 _remainingSeconds 保存当前剩余总秒数。
  • 点击 Start 后每秒递减。
  • 运行中可以 Pause。
  • 剩余时间大于 0 时可以 Reset。
  • 倒计时结束后弹出 Time's Up! 对话框。
  • 运行中 AppBar 和圆形显示区使用红色强调。

1.2 技术目标

本文围绕真实源码拆解以下内容:

  1. Flutter 应用入口和红色 Material 3 主题。
  2. _hours_minutes_seconds 如何表示用户设置值。
  3. _remainingSeconds 如何作为倒计时主状态。
  4. _updateDisplayTime 如何完成时分秒到秒的换算。
  5. _startTimer 如何使用 Future.doWhile 构建计时循环。
  6. mounted_isRunning 如何保护异步回调。
  7. _pauseTimer_resetTimer 如何修改状态。
  8. _formatTime 如何输出 HH:mm:ss
  9. _buildTimePicker 如何复用时分秒步进器。
  10. OpenHarmony 侧如何验证计时、按钮、弹窗和状态切换。

1.3 核心实现速览

能力 当前实现 适配关注点
应用入口 runApp(const CountdownTimerApp()) 确认首屏加载
主题 ColorScheme.fromSeed(seedColor: Colors.red) 确认红色主题
预设时间 _presetMinutes = [1, 5, 10, 15, 30, 60] 确认按钮设置时间
手动设置 _buildTimePicker 确认加减按钮
剩余时间 _remainingSeconds 确认计时状态
开始计时 Future.doWhile 确认每秒递减
暂停计时 _isRunning = false 确认循环退出
重置计时 所有时间归零 确认回到初始态
完成提示 showDialog + AlertDialog 确认弹窗显示
时间格式 padLeft(2, '0') 确认两位数展示

二、环境准备与工程结构

2.1 工程结构

项目保持 Flutter 标准结构,同时包含 OpenHarmony 平台工程。

文件或目录 作用
lib/main.dart 应用入口、计时状态、倒计时循环、UI 和弹窗
pubspec.yaml SDK 约束、Flutter 依赖和 Material 图标配置
analysis_options.yaml Flutter lint 规则
test/ Flutter 测试目录
ohos/ OpenHarmony 平台承载工程
README.md 项目说明文件

当前业务逻辑集中在 lib/main.dart,没有引入计时器三方库。

2.2 依赖配置

项目使用 Dart SDK ^3.9.2,依赖 Flutter SDK。

environment:
  sdk: ^3.9.2

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.8

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^5.0.0

flutter:
  uses-material-design: true

计时逻辑使用 Dart 的 Future 和 Flutter 的 setState 完成,不依赖额外插件。

2.3 常用命令

flutter pub get
flutter analyze
flutter test
flutter run
命令 用途
flutter pub get 获取依赖
flutter analyze 执行静态分析
flutter test 执行测试
flutter run 在目标设备运行

OpenHarmony 调试时,需要结合本地 Flutter OpenHarmony 工具链完成构建、安装和运行。

三、应用入口与主题配置

3.1 import 依赖

项目只引入 Flutter Material。

import 'package:flutter/material.dart';

material.dart 提供 MaterialAppScaffoldAppBarElevatedButtonIconButtonAlertDialog 等组件。

3.2 main 函数

入口函数启动根组件。

void main() {
  runApp(const CountdownTimerApp());
}

3.3 CountdownTimerApp

根组件创建 MaterialApp

class CountdownTimerApp extends StatelessWidget {
  const CountdownTimerApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Countdown Timer',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
        useMaterial3: true,
      ),
      home: const CountdownTimerHomePage(title: 'Countdown Timer'),
    );
  }
}

这段代码包含三个关键点:

  • 应用标题为 Countdown Timer
  • 使用 Colors.red 作为主题种子色。
  • 首页为 CountdownTimerHomePage

四、页面状态设计

4.1 StatefulWidget

倒计时页面有大量运行时状态,因此使用 StatefulWidget

class CountdownTimerHomePage extends StatefulWidget {
  const CountdownTimerHomePage({super.key, required this.title});
  final String title;

  
  State<CountdownTimerHomePage> createState() => _CountdownTimerHomePageState();
}

4.2 核心状态字段

状态类中定义了时间、运行状态和预设分钟列表。

class _CountdownTimerHomePageState extends State<CountdownTimerHomePage> {
  int _hours = 0;
  int _minutes = 0;
  int _seconds = 0;
  int _remainingSeconds = 0;
  bool _isRunning = false;
  bool _isSetUp = false;

  final List<int> _presetMinutes = [1, 5, 10, 15, 30, 60];
}
字段 默认值 作用
_hours 0 用户设置的小时
_minutes 0 用户设置的分钟
_seconds 0 用户设置的秒
_remainingSeconds 0 当前剩余总秒数
_isRunning false 是否正在倒计时
_isSetUp false 标记是否设置过时间
_presetMinutes [1, 5, 10, 15, 30, 60] 快捷预设分钟

4.3 主状态选择

倒计时真正递减的是 _remainingSeconds

int _remainingSeconds = 0;

这种设计比直接递减小时、分钟、秒更简单。计时循环只需要每秒执行一次 _remainingSeconds--,展示时再格式化为 HH:mm:ss

计时类应用应把“计算状态”和“展示格式”分开。当前项目用总秒数驱动计时,再用格式化函数展示,方向是正确的。

五、初始化与时间同步

5.1 initState

页面初始化时调用 _updateDisplayTime


void initState() {
  super.initState();
  _updateDisplayTime();
}

默认时、分、秒都是 0,因此初始化后的 _remainingSeconds 也是 0。

5.2 _updateDisplayTime

该方法把时分秒转换为总秒数。

void _updateDisplayTime() {
  _remainingSeconds = _hours * 3600 + _minutes * 60 + _seconds;
}

换算规则如下:

单位 换算
1 小时 3600 秒
1 分钟 60 秒
1 秒 1 秒

5.3 示例换算

1 小时 5 分 30 秒
= 1 * 3600 + 5 * 60 + 30
= 3930 秒

这个结果会写入 _remainingSeconds,后续倒计时循环只处理这个总秒数。

六、预设时间实现

6.1 预设分钟列表

项目定义了六个预设值。

final List<int> _presetMinutes = [1, 5, 10, 15, 30, 60];
预设 使用场景
1 min 快速测试
5 min 短休息
10 min 小任务
15 min 番茄钟短段
30 min 中等任务
60 min 长时间倒计时

6.2 _setPreset

点击预设按钮后,代码把分钟换算为小时和分钟。

void _setPreset(int minutes) {
  setState(() {
    _hours = minutes ~/ 60;
    _minutes = minutes % 60;
    _seconds = 0;
    _isSetUp = true;
    _updateDisplayTime();
  });
}

~/ 是 Dart 的整数除法,% 取余数。

6.3 60 分钟示例

minutes = 60
_hours = 60 ~/ 60 = 1
_minutes = 60 % 60 = 0
_seconds = 0

因此 60 分钟预设会显示为 01:00:00

七、时分秒步进器

7.1 设置区域显示条件

当没有运行且剩余时间为 0 时,页面显示设置区。

if (!_isRunning && _remainingSeconds == 0) ...[
  const Text('Set Timer'),
  // 预设按钮和时分秒步进器
]

倒计时运行中或暂停在非零剩余时间时,设置区不会显示。

7.2 _buildTimePicker

时、分、秒三个控件由同一个方法构建。

Widget _buildTimePicker(String label, int value, Function(int) onChanged, int max) {
  return Column(
    children: [
      Text(label, style: const TextStyle(fontSize: 14)),
      const SizedBox(height: 8),
      Container(
        decoration: BoxDecoration(
          border: Border.all(color: Colors.grey.shade300),
          borderRadius: BorderRadius.circular(8),
        ),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            IconButton(
              onPressed: value > 0 ? () => onChanged(value - 1) : null,
              icon: const Icon(Icons.remove),
            ),
            SizedBox(
              width: 40,
              child: Text(
                value.toString().padLeft(2, '0'),
                textAlign: TextAlign.center,
                style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
            ),
            IconButton(
              onPressed: value < max ? () => onChanged(value + 1) : null,
              icon: const Icon(Icons.add),
            ),
          ],
        ),
      ),
    ],
  );
}

7.3 小时步进器

小时最大值是 23。

_buildTimePicker('Hours', _hours, (v) {
  setState(() {
    _hours = v;
    _isSetUp = true;
  });
  _updateDisplayTime();
}, 23)

7.4 分钟和秒步进器

分钟和秒最大值都是 59。

_buildTimePicker('Minutes', _minutes, (v) {
  setState(() {
    _minutes = v;
    _isSetUp = true;
  });
  _updateDisplayTime();
}, 59)
_buildTimePicker('Seconds', _seconds, (v) {
  setState(() {
    _seconds = v;
    _isSetUp = true;
  });
  _updateDisplayTime();
}, 59)

7.5 边界控制

控件 最小值 最大值 减号状态 加号状态
Hours 0 23 value > 0 时可点 value < 23 时可点
Minutes 0 59 value > 0 时可点 value < 59 时可点
Seconds 0 59 value > 0 时可点 value < 59 时可点

边界按钮置为 null 后,Flutter 会自动展示禁用态。

八、开始计时逻辑

8.1 _startTimer 入口

开始方法先判断剩余时间。

void _startTimer() {
  if (_remainingSeconds <= 0) return;

  setState(() {
    _isRunning = true;
    _isSetUp = false;
  });

  Future.doWhile(() async {
    await Future.delayed(const Duration(seconds: 1));
    if (!mounted || !_isRunning) return false;

    setState(() {
      _remainingSeconds--;
    });

    if (_remainingSeconds <= 0) {
      _isRunning = false;
      _showCompletedDialog();
      return false;
    }
    return true;
  });
}

如果 _remainingSeconds <= 0,方法直接返回,不会启动循环。

8.2 运行状态设置

setState(() {
  _isRunning = true;
  _isSetUp = false;
});

进入运行状态后,页面会隐藏设置区,AppBar 和倒计时圆形区域会切换为红色强调。

8.3 Future.doWhile

计时循环使用 Future.doWhile

Future.doWhile(() async {
  await Future.delayed(const Duration(seconds: 1));
  if (!mounted || !_isRunning) return false;

  setState(() {
    _remainingSeconds--;
  });

  if (_remainingSeconds <= 0) {
    _isRunning = false;
    _showCompletedDialog();
    return false;
  }
  return true;
});

每次循环等待 1 秒,然后递减剩余秒数。

8.4 生命周期保护

if (!mounted || !_isRunning) return false;

mounted 用于确认页面仍在树上,_isRunning 用于确认用户没有暂停或重置。任一条件不满足,循环都会退出。

九、暂停、重置与完成

9.1 暂停逻辑

暂停只需要把 _isRunning 设置为 false

void _pauseTimer() {
  setState(() {
    _isRunning = false;
  });
}

下一次 Future.doWhile 检查 _isRunning 时会返回 false,循环终止。

9.2 重置逻辑

重置会清空所有时间状态。

void _resetTimer() {
  setState(() {
    _isRunning = false;
    _hours = 0;
    _minutes = 0;
    _seconds = 0;
    _remainingSeconds = 0;
    _isSetUp = false;
  });
}

重置后页面回到初始设置状态。

9.3 完成弹窗

倒计时归零后显示弹窗。

void _showCompletedDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('Time\'s Up!'),
      content: const Text('Your countdown timer has finished.'),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            _resetTimer();
          },
          child: const Text('OK'),
        ),
      ],
    ),
  );
}

用户点击 OK 后关闭弹窗并重置计时器。

十、时间格式化

10.1 _formatTime

剩余秒数通过 _formatTime 转为 HH:mm:ss

String _formatTime() {
  final h = (_remainingSeconds ~/ 3600).toString().padLeft(2, '0');
  final m = ((_remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0');
  final s = (_remainingSeconds % 60).toString().padLeft(2, '0');
  return '$h:$m:$s';
}

10.2 小时计算

final h = (_remainingSeconds ~/ 3600).toString().padLeft(2, '0');

整数除法得到剩余小时。

10.3 分钟和秒计算

final m = ((_remainingSeconds % 3600) ~/ 60).toString().padLeft(2, '0');
final s = (_remainingSeconds % 60).toString().padLeft(2, '0');

分钟先对 3600 取余,再除以 60;秒直接对 60 取余。

10.4 格式化示例

_remainingSeconds 输出
0 00:00:00
5 00:00:05
65 00:01:05
3600 01:00:00
3930 01:05:30

padLeft(2, '0') 保证每个部分至少两位。

十一、显示区域与状态样式

11.1 AppBar 状态色

运行中 AppBar 使用红色。

appBar: AppBar(
  title: Text(widget.title),
  backgroundColor: _isRunning ? Colors.red : Theme.of(context).colorScheme.inversePrimary,
)

未运行时使用主题的 inversePrimary

11.2 圆形倒计时区域

倒计时数字显示在圆形区域中。

Container(
  padding: const EdgeInsets.all(32),
  decoration: BoxDecoration(
    shape: BoxShape.circle,
    color: _isRunning ? Colors.red.shade50 : Colors.grey.shade100,
    border: Border.all(
      color: _isRunning ? Colors.red : Colors.grey.shade300,
      width: 4,
    ),
  ),
  child: Text(
    _formatTime(),
    style: TextStyle(
      fontSize: 56,
      fontWeight: FontWeight.bold,
      fontFamily: 'monospace',
      color: _isRunning ? Colors.red : Colors.black,
    ),
  ),
)

运行中背景变浅红、边框变红、文字变红,状态变化明显。

11.3 等宽字体

fontFamily: 'monospace'

等宽字体适合计时器,因为数字变化时宽度更稳定,界面不容易抖动。

十二、按钮显隐逻辑

12.1 Start 按钮

未运行且剩余时间大于 0 时显示 Start。

if (!_isRunning && _remainingSeconds > 0)
  ElevatedButton.icon(
    onPressed: _startTimer,
    icon: const Icon(Icons.play_arrow),
    label: const Text('Start'),
    style: ElevatedButton.styleFrom(
      padding: const EdgeInsets.all(16),
      backgroundColor: Colors.green,
    ),
  )

12.2 Pause 按钮

运行中显示 Pause。

if (_isRunning)
  ElevatedButton.icon(
    onPressed: _pauseTimer,
    icon: const Icon(Icons.pause),
    label: const Text('Pause'),
    style: ElevatedButton.styleFrom(
      padding: const EdgeInsets.all(16),
      backgroundColor: Colors.orange,
    ),
  )

12.3 Reset 按钮

剩余时间大于 0 时显示 Reset。

if (_remainingSeconds > 0)
  ElevatedButton.icon(
    onPressed: _resetTimer,
    icon: const Icon(Icons.refresh),
    label: const Text('Reset'),
    style: ElevatedButton.styleFrom(
      padding: const EdgeInsets.all(16),
      backgroundColor: Colors.grey,
    ),
  )

12.4 状态组合表

状态 条件 可见按钮
初始 _remainingSeconds == 0 且未运行 无按钮
已设置未开始 _remainingSeconds > 0 且未运行 Start、Reset
运行中 _isRunning == true Pause、Reset
暂停 _remainingSeconds > 0 且未运行 Start、Reset
完成 _remainingSeconds <= 0 弹窗后重置

按钮显隐围绕 _isRunning_remainingSeconds 展开,逻辑清晰。

十三、OpenHarmony 适配要点

13.1 基础组件验证

当前项目使用的 Flutter 组件包括:

组件 作用 OpenHarmony 关注点
MaterialApp 应用根组件 首屏加载
Scaffold 页面骨架 AppBar 与 Body
Wrap 预设按钮换行 小屏布局
ElevatedButton.icon 操作按钮 点击响应
IconButton 步进器加减 禁用态和点击
Container 圆形倒计时显示 圆形、边框、颜色
AlertDialog 完成提示 弹窗显示
TextButton 弹窗确认 关闭和重置

13.2 计时链路验证

OpenHarmony 上应重点验证:

  1. 选择 1 分钟预设后显示 00:01:00
  2. 点击 Start 后每秒递减。
  3. 运行中 AppBar 变红。
  4. 运行中圆形显示区变红。
  5. 点击 Pause 后数字停止变化。
  6. 再次点击 Start 后从剩余时间继续。
  7. 点击 Reset 后回到 00:00:00
  8. 倒计时结束后出现 Time's Up! 弹窗。

13.3 步进器验证

手动设置需要覆盖:

  • Hours 加到 23 后加号禁用。
  • Hours 为 0 时减号禁用。
  • Minutes 加到 59 后加号禁用。
  • Seconds 加到 59 后加号禁用。
  • 调整任意值后圆形显示区同步刷新。

13.4 生命周期验证

倒计时运行中如果离开页面,异步循环会检查 mounted

if (!mounted || !_isRunning) return false;

这能避免页面销毁后继续调用 setState。OpenHarmony 适配时,应关注返回、切后台和页面销毁场景。

计时器适配要验证时间准确性、状态切换和生命周期安全。只验证按钮能点是不够的。

十四、测试与验证

14.1 初始页面测试

Widget 测试可以验证首屏结构。

import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('countdown timer shows initial widgets', (tester) async {
    await tester.pumpWidget(const CountdownTimerApp());

    expect(find.text('Countdown Timer'), findsWidgets);
    expect(find.text('Set Timer'), findsOneWidget);
    expect(find.text('00:00:00'), findsOneWidget);
    expect(find.text('1 min'), findsOneWidget);
    expect(find.text('60 min'), findsOneWidget);
  });
}

这类测试不依赖真实时间流逝,稳定性较高。

14.2 预设按钮测试

可以验证点击 5 分钟后显示 00:05:00

testWidgets('preset button sets remaining time', (tester) async {
  await tester.pumpWidget(const CountdownTimerApp());

  await tester.tap(find.text('5 min'));
  await tester.pump();

  expect(find.text('00:05:00'), findsOneWidget);
  expect(find.text('Start'), findsOneWidget);
  expect(find.text('Reset'), findsOneWidget);
});

14.3 计时递减测试

可以点击 1 分钟预设并启动。

testWidgets('timer decreases after start', (tester) async {
  await tester.pumpWidget(const CountdownTimerApp());

  await tester.tap(find.text('1 min'));
  await tester.pump();
  await tester.tap(find.text('Start'));
  await tester.pump();
  await tester.pump(const Duration(seconds: 1));

  expect(find.text('00:00:59'), findsOneWidget);
});

这类测试需要让测试时钟推进。

14.4 手工验证矩阵

场景 操作 预期
首次打开 启动应用 显示 00:00:00 和设置区
预设 10 分钟 点击 10 min 显示 00:10:00
手动设置 点击秒加号 秒数增加
开始 点击 Start 每秒递减
暂停 点击 Pause 数字停止
继续 再点 Start 从剩余时间继续
重置 点击 Reset 回到 00:00:00
完成 等待归零 显示完成弹窗

十五、常见问题与优化建议

15.1 为什么用总秒数驱动倒计时

总秒数适合递减逻辑。

_remainingSeconds--;

如果直接递减时、分、秒,需要处理借位逻辑,代码更复杂。

15.2 为什么使用 Future.doWhile

Future.doWhile 可以表达“等待 1 秒、检查状态、更新 UI、决定是否继续”的循环。

Future.doWhile(() async {
  await Future.delayed(const Duration(seconds: 1));
  return true;
});

当前代码通过返回 false 停止循环。

15.3 为什么暂停只改 _isRunning

暂停时不清空剩余时间,只让循环退出。

setState(() {
  _isRunning = false;
});

剩余秒数保留,因此再次 Start 可以继续计时。

15.4 _isSetUp 当前有什么作用

当前源码中 _isSetUp 会被赋值,但 UI 主要依赖 _isRunning_remainingSeconds。因此它目前没有明显参与页面决策。

bool _isSetUp = false;

后续可以删除该字段,或者用它显示“已设置,点击开始”的提示。

15.5 如何提升计时精度

当前实现每次循环都等待 1 秒,然后递减 1。复杂场景下可以记录开始时间和目标结束时间,用系统时间差计算剩余时间。

final endTime = DateTime.now().add(Duration(seconds: _remainingSeconds));
final remaining = endTime.difference(DateTime.now()).inSeconds;

这种方式对短暂停顿、调度延迟更稳。

15.6 如何增加声音或震动提示

倒计时结束后当前只弹窗。后续可以接入声音、震动或系统通知能力。

void onTimerFinished() {
  _showCompletedDialog();
}

OpenHarmony 上实现这些能力时,需要结合平台权限和插件适配情况。

十六、工程扩展方向

16.1 抽取计时状态模型

可以把状态建模为对象。

class CountdownState {
  final int remainingSeconds;
  final bool isRunning;

  const CountdownState({
    required this.remainingSeconds,
    required this.isRunning,
  });
}

模型化后,UI 状态判断会更集中。

16.2 抽取时间格式化函数

时间格式化可以抽成纯函数。

String formatDurationSeconds(int seconds) {
  final h = (seconds ~/ 3600).toString().padLeft(2, '0');
  final m = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0');
  final s = (seconds % 60).toString().padLeft(2, '0');
  return '$h:$m:$s';
}

纯函数更容易写单元测试。

16.3 增加自定义预设

可以允许用户保存常用倒计时。

class TimerPreset {
  final String name;
  final int seconds;

  const TimerPreset({
    required this.name,
    required this.seconds,
  });
}

例如“泡茶 3 分钟”“运动 20 分钟”“休息 5 分钟”。

16.4 增加进度环

当前圆形区域只展示时间,没有显示进度比例。可以保存初始总秒数,再计算剩余比例。

final progress = totalSeconds == 0 ? 0.0 : remainingSeconds / totalSeconds;

配合 CircularProgressIndicator 或自定义绘制,可以形成更直观的倒计时进度环。

总结

countdown_timer 是一个结构清晰的 Flutter 倒计时器案例。它用 _hours_minutes_seconds 表示用户设置时间,用 _remainingSeconds 作为倒计时主状态,用 _isRunning 控制运行和暂停,用 _updateDisplayTime 完成时分秒到总秒数的换算,用 _formatTime 输出 HH:mm:ss,再用 Future.doWhileFuture.delayed 每秒递减。

从 OpenHarmony 适配角度看,这个项目适合验证 Flutter 预设按钮、步进器、条件渲染、异步计时、弹窗、按钮显隐、红色运行态和生命周期保护。排查路径也比较直接:时间不对看 _remainingSeconds,按钮不对看 _isRunning 和剩余秒数,弹窗不出看归零分支,页面销毁异常看 mounted 保护。

掌握这个项目后,可以继续扩展进度环、自定义预设、声音提示、震动提醒、后台通知和更精确的时间差计算,让倒计时器从演示应用逐步演进为更完整的跨平台计时工具。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐