Flutter 鸿蒙插件适配实战:用 flutter_timezone_observer 1.2.0 监听系统时区变化
适配仓库: https://atomgit.com/oh-flutter/flutter_timezone_observer
适配分支:
feat/ohos_flutter_timezone_observer_1.2.0
一、最终效果与适配目标
日历、航班、跨国会议类应用启动时要知道当前时区,运行期间还要应对用户切换系统设置。只在启动时读一次,会让后续时间换算继续使用旧时区;只订阅事件,又拿不到订阅前的初始值。flutter_timezone_observer 1.2.0 已经提供 currentTimezone、onTimezoneChanged 和两个生命周期 mixin,但没有 OHOS 原生实现。
这次适配保留所有 Dart API:方法通道负责当前值,事件通道负责变化。事件到达后重新向系统读取时区,而不是相信广播附带的旧参数。插件只读不写,不会修改设备时区。

图 1:真机宿主显示当前时区与变化事件。
| 验证点 | 实测结果 | 证据 |
|---|---|---|
| 当前时区读取 | Asia/Shanghai 与 Asia/Tokyo 均能按设置读取 | 图 1、图 6 |
| 系统时区变化 | 订阅期间收到真实变化事件 | 图 6 |
| 取消订阅 | 设置变化但事件计数不增加 | 图 6 |
| 重新订阅 | 不伪造初始事件,后续真实变化可再次收到 | 图 6 |
| 自动化与构建 | 30 项 Dart/Widget/ArkTS 测试及 HAP 构建通过 | 图 4、图 5 |
成果速览
| 项目 | 内容 |
|---|---|
| 上游基线 | TAG v1.2.0,提交 a9bf5cd004f09001d03983d1b326cdcad7548271,MIT |
| 适配分支 | feat/ohos_flutter_timezone_observer_1.2.0 |
| 适配 TAG | 尚未发布 |
| 真机受测提交 | cfd089e46aa849ab0ff8aee6f6126fcb45d7e0af |
| 当前分支 HEAD | 8e2a78dea4402d02375c107c8a9f13cfc330d6a8,后续仅追加验证文档 |
| 新增 OHOS 能力 | 当前值读取、时区变化事件、取消和重订阅 |
| 真机边界 | 不保证后台期间每一条历史事件都能补发 |
二、测试环境
| 项目 | 实测版本 |
|---|---|
| Flutter OH | 3.41.10-ohos-1.0.1 |
| Dart | 3.11.5 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26,示例兼容 API 18 |
| 真机 | CHZ-AL00,HarmonyOS 7.0.0.105 |
| 插件 | flutter_timezone_observer 1.2.0 |
环境搭建参考 Flutter OH 指南。截至 2026 年 9 月 12 日,版本号最大的标签是预览版 3.44.9+ohos-0.0.1-canary1,最新正式稳定标签仍是本文实测的 3.41.10-ohos-1.0.1。
三、仓库基线和分支
上游标签 v1.2.0 对应 a9bf5cd004f09001d03983d1b326cdcad7548271。适配前核对公开源码、MIT 许可证、待适配清单及组织仓库,在当日搜索范围内未发现同包适配。代码同步到 AtomGit 后保留上游历史。
git clone https://atomgit.com/oh-flutter/flutter_timezone_observer.git
cd flutter_timezone_observer
git switch feat/ohos_flutter_timezone_observer_1.2.0
git remote -v
复现分支和 OHOS 插件结构:
git switch -c feat/ohos_flutter_timezone_observer_1.2.0 a9bf5cd004f09001d03983d1b326cdcad7548271
flutter create --template=plugin --platforms=ohos --no-pub .
图 2 汇总了实际 AtomGit origin、适配分支和 HEAD,可核对仓库来源与当前分支状态。

图 2:AtomGit origin、适配分支和 HEAD。
四、原 Dart 接口与双通道
公共用法没有变化:
final current = await FlutterTimezoneObserver.currentTimezone;
final subscription = FlutterTimezoneObserver.onTimezoneChanged.listen(
(ianaZone) => print(ianaZone),
onError: (Object error) => print(error),
);
await subscription.cancel();
内部方法通道是 flutter_timezone_observer/methods,方法名 getLocalTimezone;事件通道是 flutter_timezone_observer/events。单独订阅不会主动发初始值,初始化要先读 currentTimezone,或者使用项目已有 mixin。Dart getter 在平台异常时沿用上游逻辑返回 unknown,事件流错误则交给订阅方处理。
五、读取和订阅的 ArkTS 实现
pubspec.yaml 注册 FlutterTimezoneObserverPlugin。当前值由 systemDateTime.getTimezoneSync() 返回,空字符串视为错误。事件部分订阅 COMMON_EVENT_TIMEZONE_CHANGED:
const subscriber = commonEventManager.createSubscriberSync({
events: [commonEventManager.Support.COMMON_EVENT_TIMEZONE_CHANGED],
});
this.subscriber = subscriber;
commonEventManager.subscribe(subscriber, (error, data) => {
if (this.subscriber !== subscriber) {
return;
}
if (data.event === commonEventManager.Support.COMMON_EVENT_TIMEZONE_CHANGED) {
this.eventSink?.success(this.readTimezone());
}
});
同步创建 subscriber 是刻意选择:如果创建本身是异步的,用户可能已经取消或 Engine 已解绑,晚到的 subscriber 又被注册回来。回调里还要比较对象身份,防止旧订阅的事件流入新 EventSink。
取消、替换订阅和引擎分离都会调用 unsubscribe 并清空引用。订阅失败用 timezone_subscription_error,读取失败用 timezone_error。应用挂起期间系统可能限制事件投递,所以 AppLifecycleAwareTimezoneObserverMixin 回前台后还会主动读取一次;它不承诺补齐后台错过的每一条历史事件。

图 3:COMMON_EVENT_TIMEZONE_CHANGED 订阅、取消与 EventSink 返回路径。
六、仓库交付检查
本分支保留原 Dart API、mixin、Android/iOS/Web 实现和 MIT 许可证,新增 OHOS HAR、示例工程、双语适配文档、Dart/Widget 测试、生产 ArkTS 生命周期测试以及设备测试入口。示例可以展示初始值、事件值、暂停和恢复订阅。
没有新增修改系统时区的权限,也不提交签名、本机 SDK 地址、依赖缓存或构建产物。
七、测试和 HAP 构建
flutter pub get
flutter analyze
flutter test
npm install --prefix ohos/test --no-save --no-package-lock typescript@5.9.3
node --test ohos/test/flutter_timezone_observer_lifecycle.test.cjs
cd example
flutter test
flutter build hap --debug --no-codesign
17 项 Dart 测试、1 项示例 widget 测试和 12 项生产 ArkTS 生命周期测试通过,共 30 项;静态分析和无签名 HAP 构建通过。原生测试覆盖读取、事件、取消、重订阅、同步/异步失败清理、旧回调隔离和引擎分离。
真机消费的是远程代码 cfd089e46aa849ab0ff8aee6f6126fcb45d7e0af。最终 HEAD 8e2a78d 只追加验证文档,不代表对代码再次构建。

图 4:Flutter 复跑与 30 项 Dart/ArkTS 用例统计。

图 5:HAP 产物元数据及真机宿主的 resolved-ref。
八、真实系统时区变化验收
dependencies:
flutter_timezone_observer:
git:
url: https://atomgit.com/oh-flutter/flutter_timezone_observer.git
ref: cfd089e46aa849ab0ff8aee6f6126fcb45d7e0af
执行 flutter pub get 后,应检查 pubspec.lock 的 resolved-ref 是否等于上述真机受测 SHA。当前没有适配 TAG,不应让分支后续变化悄悄改变业务构建。
真机初始为 Asia/Shanghai。通过系统设置切换到大阪后,旧宿主收到 Asia/Tokyo 事件;增强宿主启动后,observer 与 timezone_provider 都读取到 Asia/Tokyo。随后恢复上海,收到 Asia/Shanghai,计数为 1。取消订阅期间再次切到东京,读取值变化但事件计数仍为 1;约 30 秒后重订阅没有伪造初始事件;再次恢复上海后收到第二个事件。
结束时已恢复 Asia/Shanghai 和自动设置开启,并与原布局核对。触发全部来自系统设置页面,不是伪广播。仓库 integration 与 Hypium 套件没有执行,文章应把隔离宿主验收单独表述。

图 6:上海/东京变化、取消期间无事件和恢复设置的记录。
九、提交分支
git status --short
git add pubspec.yaml ohos example test README.OpenHarmony.md README.OpenHarmony_CN.md
git commit -m "feat: add OHOS support for flutter_timezone_observer"
git push -u origin feat/ohos_flutter_timezone_observer_1.2.0
仓库已公开,适配分支是默认分支。2026 年 9 月 12 日匿名读取 HEAD 为 8e2a78dea4402d02375c107c8a9f13cfc330d6a8。
十、FAQ
Q1:订阅后为什么没有立即收到当前值
- 现象: stream 建立后没有第一条事件。
- 原因: 上游约定事件流只报告变化,不发送初始快照。
- 解决方法: 初始化时读取
currentTimezone,或使用生命周期 mixin。 - 验证结果: 真机重订阅后计数保持不变,没有伪造事件。
Q2:取消订阅后读取值变了,事件计数却没变
- 现象: 当前时区是东京,事件仍停在 1。
- 原因: 按请求读取和事件订阅是两条独立路径,取消只停止事件。
- 解决方法: 需要当前状态时主动读;需要后续通知时重新订阅。
- 验证结果: 真机明确覆盖了取消期间切换时区。
Q3:为什么收到事件后还要重新读取系统值
- 现象: 公共事件本身可能带参数。
- 原因: 参数可能缺失或已经过期,系统当前值才是最终状态。
- 解决方法: 在 callback 中调用
getTimezoneSync()。 - 验证结果: 每次事件值都与两个独立读取入口一致。
十一、总结
flutter_timezone_observer 的 OHOS 适配补齐了当前值读取和真实变化事件,同时保留取消、重订阅及前台恢复语义。30 项自动化、HAP 构建和系统设置触发的真机变化都已验证。它不修改系统配置,也不保证后台期间每条事件都能投递;业务应把“当前状态”和“变化通知”组合使用。
十二、参考链接
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐



所有评论(0)