适配仓库: https://atomgit.com/oh-flutter/flutter_timezone_observer

适配分支: feat/ohos_flutter_timezone_observer_1.2.0

一、最终效果与适配目标

日历、航班、跨国会议类应用启动时要知道当前时区,运行期间还要应对用户切换系统设置。只在启动时读一次,会让后续时间换算继续使用旧时区;只订阅事件,又拿不到订阅前的初始值。flutter_timezone_observer 1.2.0 已经提供 currentTimezoneonTimezoneChanged 和两个生命周期 mixin,但没有 OHOS 原生实现。

这次适配保留所有 Dart API:方法通道负责当前值,事件通道负责变化。事件到达后重新向系统读取时区,而不是相信广播附带的旧参数。插件只读不写,不会修改设备时区。

在这里插入图片描述

图 1:真机宿主显示当前时区与变化事件。

验证点实测结果证据
当前时区读取Asia/ShanghaiAsia/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
当前分支 HEAD8e2a78dea4402d02375c107c8a9f13cfc330d6a8,后续仅追加验证文档
新增 OHOS 能力当前值读取、时区变化事件、取消和重订阅
真机边界不保证后台期间每一条历史事件都能补发

二、测试环境

项目实测版本
Flutter OH3.41.10-ohos-1.0.1
Dart3.11.5
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 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.lockresolved-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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐