Flutter 适配鸿蒙手表屏幕形状:让计时卡片按圆屏和方屏布局

📦 Flutter 三方库 wear_plus 用于穿戴页面的屏幕形状判断,但选定的上游版本尚未适配鸿蒙。我在原库基础上补上鸿蒙系统形状查询,让页面继续通过 WatchShape 选择布局。
完整代码已开源在 oh-flutter/wear_plus,可直接使用。有问题可以到仓库提 Issue,也可以来 CPF-Flutter 鸿蒙社区 交流 Flutter 鸿蒙适配问题。

前言

我在做一个 Flutter 手表计时示例:中间显示时间,下面放“记圈”和“重置”。把它装到 Watch 4 Pro 后,页面能按圆屏布局显示,可查询屏幕形状的通道却报了 MissingPluginException。界面看着正常,底下的查询怎么没接通?🔍

我顺着 WatchShape 查源码,发现组件先用圆屏作为默认值,再异步查询系统;选定的 wear_plus 版本只注册了 Android,鸿蒙端还没有处理器。于是,我保留原来的 Dart 接口,补上鸿蒙形状查询,再用同一个计时页面做对照。接入后,独立诊断从“查询未接入”变成“圆屏已确认”,日志也收到了系统返回的 round。两组原本都能记圈和重置,这次补齐的是形状查询;默认圆屏布局和系统确认结果的区别,也成了我想讲清楚的地方。

手表吉祥物与屏幕形状适配流程

图1 手表形状适配

一、计时卡片为什么需要知道屏幕形状

1.1 同一张卡片放到两种屏幕上

我把复现页面收成了一个小计时器:打开后从零开始,点“记圈”留下这一圈的用时,点“重置”开始下一轮。跑步或间歇训练时可以使用这样的交互,不过本文实际操作的是模拟器上的 Example。页面的重点是让时间、记录和按钮在小屏上有清楚的位置,运动传感器和配速计算留给具体应用。

如果把所有内容都放进靠近矩形窗口四角的位置,圆屏页面可能显示不全;如果始终按圆屏内接区域收缩,矩形屏又会留下不必要的空白。应用需要一条可复用的判断依据,然后自己决定按钮、文字和边距。我希望判断依据能跟着设备走,换型号时仍然适用。

场景页面要完成的操作本次关注的问题
圆屏计时读时间、记圈、重置关键内容放在圆形显示区域以内
矩形屏复用使用相同的计时逻辑根据形状选择边距,保留可用空间
迁移已有穿戴页面沿用 WatchShape鸿蒙端能否响应原有通道请求

表1 页面使用场景

Flutter 的窗口尺寸描述的是布局可用的矩形。宽高相等可以说明这个矩形是正方形,不能单独说明设备的屏幕是圆形。Watch 4 Pro 这次回读的显示宽高是 466 × 466,独立探针还读到了 screenShape: 1。形状属性补充的是尺寸里没有的信息。安全区、窗口大小和物理屏幕形状也各有用途,不能拿其中一个字段替代其他两个。

我把责任分在三层:

  • 系统:提供屏幕形状。
  • 插件:把原生枚举转换为上游约定的字符串。
  • Example:消费 WatchShape,计算适合当前形状的内容区域。

计时、记圈和按钮样式都属于应用。读回一个 round 也不会让任意 Flutter 页面自动适配圆屏,布局仍然需要应用处理。

1.2 选择既有插件,先限定这次补齐什么

wear_plus 原本面向 Google Wear OS,使用 BSD-3-Clause 许可。它已有 Wear.instance.getShape() 和 WatchShape,适合继续承接屏幕形状查询。固定的 1.2.4 源码中,Dart 通过 MethodChannel('wear') 调用 getShape,pubspec.yaml 只注册 Android。这是本次增加平台实现的出发点:公开接口存在,鸿蒙系统也有对应的公开查询能力,中间缺少鸿蒙处理器。

wear_plus 1.2.5 的 pub.dev 包页面及 Android 平台标签

图2 上游包页面

截图展示 wear_plus 1.2.5 的包页面,平台标签为 Android;本文的适配基线仍为 1.2.4,版本选择见下文。

因此,我把这项工作定位为平台能力扩展。上游原有的支持范围是 Wear OS,在鸿蒙宿主里出现 MissingPluginException,说明需要增加对应的处理器。页面怎样利用返回值,仍然沿用原来的 Flutter 组件和应用布局。

我检查了 oh-Flutter、CPF-Flutter 的公开仓库清单,分别取得 399 项和 394 项,再按穿戴关键词筛选名称与描述。在这些仓库和候选包中,我没有找到可直接使用的 wear_plus 鸿蒙移植实现。对表中的五个候选,我继续核对发布源包中的接口、平台注册和实现职责;这个结论限定于上述检索范围。

候选与核对版本与本场景的关系本次处理
wear_plus 1.2.4 / 1.2.5已有形状 API 和 WatchShape固定兼容版本,增加鸿蒙实现
wear 1.1.0早期同类穿戴包比较维护与接口来源
is_wear 0.0.2+3判断设备是否属于穿戴设备不返回圆形、矩形信息
wearable_rotary 2.0.2 / 2.0.4处理表冠输入不承担屏幕形状判断
flutter_wear_os_connectivity 1.0.0处理手机与手表通信与本次本机布局查询职责不同

表2 候选职责对照

旧包的版本约束值得单独看。wear 1.1.0 写着 Dart SDK >=2.12.0 ❤️.0.0。按照 Dart 3 迁移说明,pub 会把这类空安全包的旧式上限解释为 <4.0.0,具体兼容性仍要结合依赖和源码检查。我选择 wear_plus 的依据是它的维护来源、与早期包的继承关系,以及已有的 WatchShape 用法。表里的其他包各有职责,判断设备类型、读取表冠和连接手机,都不能直接提供这里需要的屏幕形状。

我检查的上游 master 分支与 1.2.5 标签指向同一版本,另外核对了公开列表中的全部 8 个分支,都没有发现 ohos 目录。社区仓库 oh-flutter/wear_plus 导入上游源码后建立了 ohos/wear_plus 分支作为鸿蒙适配接收基线,合入前同样只保留原有 Android 目录。所用版本与各分支快照的对应关系保存在工程说明中。

版本方面,本文对照实验固定 wear_plus 1.2.4。对照的上游 1.2.5 将 Dart 下限提高到 ^3.12.0、Flutter 下限提高到 >=3.44.0,还调整了 Android Gradle 构建配置;两个版本的差异中,Dart 业务逻辑和方法签名保持一致,也不含 Dart 3.12 专有语法。

这次使用 Flutter 3.41.9+ohos-1.0.2 和 Dart 3.11.5,对照组保留 1.2.4 的原始环境声明完成对比验证;向社区 ohos/wear_plus 分支贡献的适配分支则同步基于 1.2.5 放宽环境约束,并补齐鸿蒙实现与开源合规文档,让当前社区工具链也能直接解析构建。

1.3 对照实验要保留上游的真实默认行为

只看圆屏上的首帧,会漏掉一个问题。上游 WatchShape 先把 _shape 初始化为 WearShape.round,随后才异步查询平台。即使插件还没接入,页面也可以先按圆屏布局显示。

⚠️ 易错提醒:看起来放得下,不代表底层查询已经成功。

我接着对照通道记录。baseline 中的 MissingPluginException 说明系统查询尚未接通,而默认形状仍留在组件里。因此这次保留这份默认行为,并在同一页面加上独立的查询状态:原版显示“查询未接入”,适配版收到回执后显示“圆屏已确认”。两组在圆表上都采用安全布局,差别通过状态和日志来核对。

二、同一页面上的调用结果与运行范围

2.1 页面相同,诊断状态分别记录

两组应用使用不同包名便于并排安装:baseline 是 com.example.wearbaseline,patched 是 com.example.wearpatched。除平台实现和必要的包标识外,计时页面、只读探针、按钮操作以及宿主设备声明保持一致。画面直接采集模拟器窗口,保留设备外框;时间从零累计,记圈记录来自按钮操作。

原版在圆表上的默认布局与通道诊断

图3 原版调用记录

适配版在圆表上的系统回执与计时操作

图4 适配调用记录

两段动图中的差别可以这样核对:

  • baseline:默认圆屏布局和“查询未接入”同时出现。
  • patched:原始查询收到 round,状态改为“圆屏已确认”,页面继续使用圆屏布局。

随后点“记圈”,记录行出现圈数和这一圈的用时;点“重置”,记录清空,计时重新开始。两段动图展示的是这些操作与状态变化,体验结论限定在这次模拟器。

圆屏计时页面默认布局

图5 原版圆屏默认

圆屏计时页面系统确认状态

图6 系统确认圆屏

适配版计时页面的记圈结果

图7 记圈操作结果

页面里有两条查询路径,用途分别是:

  • 布局:WatchShape 仍是应用的布局入口。
  • 诊断:Example 另外直接调用一次原始 MethodChannel('wear'),仅记录这次通道的成功或错误,不把诊断结果反过来改变布局。

这样既能验证上游组件用法,也不会把公开 API 的默认返回误认成原生查询成功。

这次 patched 日志因此有两次原生 getShape resolved round:一次由组件初始化触发,一次来自独立诊断。这是 Example 的两次请求,不是事件重复推送。普通应用只消费 WatchShape 时,可以去掉诊断查询。两组操作记录都包含连续记到第 5 圈、重置、再次记到第 1 圈;按钮功能在原版页面里同样可用。

2.2 日志只回答它真正记录的问题

屏幕形状查询与计时操作日志

图8 形状调用日志

核对项baselinepatched
独立显示探针读取系统的尺寸、密度和形状使用相同探针
原始 getShape 通道MissingPluginException返回 round
WatchShape 圆屏布局初始默认值,没有系统确认与系统查询结果一致
记圈、重置属于相同的页面逻辑属于相同的页面逻辑

表3 两组核对项目

独立探针运行在 Example 宿主中,只读取显示属性,不注册 wear 通道。它能提供参照,但不能替插件完成被测请求。只有 patched 的原始 getShape 收到正确响应,才说明新增原生模块参与了调用。主机测试里返回一个模拟的 round,也不能代替这一条设备记录。

读这份日志时,我先看探针确认的形状,再找原始请求的结果,最后核对按钮操作。时间戳用于关联先后顺序;探针与查询结果之间还包含其他代码和日志转发,不适合作为 API 耗时。这里要确认的是插件是否响应了正确的方法、是否返回约定的值。

2.3 环境与测试层次

插件声明和穿戴宿主配置

图9 插件宿主配置

项目测试环境
主机macOS,Apple Silicon arm64
DevEco Studio26.0.0.865
Flutter / Dart3.41.9+ohos-1.0.2 / 3.11.5
编译 SDK / 最低兼容 APIAPI 26 / API 18
固定插件wear_plus 1.2.4
模拟器Watch 4 Pro,wearable,466 × 466,320dpi
系统OpenHarmony-7.0.0.105,API 26
构建方式debug,无签名 HAP

表4 测试运行环境

验证先从主机测试开始:

  • 插件测试,5 项:检查通道返回与组件形状。
  • 页面测试,5 项:覆盖圆屏和矩形布局、查询失败、未知返回,以及连续记圈和重置。
  • 原生模拟分支,7 项:检查枚举映射、错误码和解绑。

三组均已通过,静态分析没有发现问题。随后,我在 ASCII 路径的干净副本构建两组 HAP,再安装到模拟器核对注册与系统调用。

原生脚本里的 display、hilog 和通道都是替身,便于模拟矩形枚举、字段缺失及系统抛错。它验证的是分支逻辑,完整的 ArkTS 工程兼容性仍由 HAP 构建检查。当前设备成功路径在圆屏模拟器取得,矩形手表只覆盖了对应的主机用例。

三、从系统形状到 Flutter 布局

3.1 保持协议,补上原生处理器

屏幕形状查询的跨端调用流程

图10 形状调用流程

鸿蒙的 Display.screenShape 从 API 18 开始提供,但属性可能缺失。插件把圆形枚举转换成 round,把矩形枚举转换成 square,沿用上游协议。这里的 square 包含矩形屏,不能再用宽高相等来限制返回。

引擎挂载时,插件建立 wear 通道并安装处理器;引擎卸载时,清除处理器和引用。每次查询通过 getDefaultDisplaySync() 读取默认显示属性,再返回结果。这里没有显示事件订阅,也不持有页面上下文,因此无需增加暂停、恢复一类长驻监听协议。

公开注册在原 pubspec.yaml 的 flutter.plugin.platforms 中增加 ohos,将 pluginClass 指向 WearPlusPlugin,原有 Android 注册项保持不变。完整配置与 HAR 导出、宿主和测试都在仓库中。

核心查询由 handleGetShape 完成。下面只去掉诊断日志,保留系统读取、枚举映射、错误回执和提前返回;通道注册与日志见完整插件源码。

private handleGetShape(result: MethodResult): void {
  try {
    const defaultDisplay: display.Display = display.getDefaultDisplaySync();
    const rawShape: display.ScreenShape | undefined = defaultDisplay.screenShape;
    // 只接受系统给出的已知形状,缺失值不当作圆屏。
    if (rawShape === display.ScreenShape.ROUND) {
      result.success('round');
      return;
    }
    if (rawShape === display.ScreenShape.RECTANGLE) {
      // 沿用上游命名:square 在这里也表示矩形屏。
      result.success('square');
      return;
    }
    result.error(
      'DISPLAY_SHAPE_UNAVAILABLE',
      `Display.screenShape is unavailable or unrecognized: ${String(rawShape)}`,
      null
    );
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    result.error('DISPLAY_READ_FAILED', `Failed to query display shape: ${message}`, null);
  }
}

这段代码区分两类失败:

  • DISPLAY_SHAPE_UNAVAILABLE:取得了显示对象,但形状不在已知枚举中。
  • DISPLAY_READ_FAILED:查询本身失败。

两个错误码把“属性不支持”和“读取过程失败”分开,排查时可以沿不同入口继续找原因。每条分支只回复一次;遇到未知枚举时保留错误,让调用端决定怎样处理。

3.2 上游默认值与成功回执要分开看

保持 API 不变也意味着保留原有行为。Wear.getShape() 捕获 PlatformException 后,会记录错误并返回 'round'。以下是固定上游版本的完整函数,注释改为中文:

Future<String> getShape() async {
    try {
      return (await _channel.invokeMethod<String>('getShape'))!;
    } on PlatformException catch (e, st) {
      // 系统查询报错时保留圆屏兜底,不代表硬件查询成功。
      debugPrint('Error calling getShape: $e\n$st');
      return 'round';
    }
  }

所以,调用公开方法得到 round 有两种可能:

  • 平台确实返回圆屏。
  • 平台返回错误,由 Dart 层选择圆屏兜底。

兜底结果不是硬件事实。它可以帮助页面保守布局;但字段缺失时,即使鸿蒙实现返回了明确错误码,公开 Dart API 仍可能返回圆屏,这一点必须告诉接入者。

MissingPluginException 又是另一种情况。它不属于这里捕获的 PlatformException,固定版本不会在这条路径中返回 'round'。与此同时,WatchShape 的状态本来就初始化为圆屏;异步调用失败不等于首帧从未显示。baseline 可以同时出现“页面按圆屏布局”和“异步通道报错”,两者并不矛盾。

我保留了上游已有的异常处理和默认形状语义。要让业务明确区分“系统确认”和“兜底结果”,需要另行设计兼容的状态 API,并讨论旧调用者如何迁移。那属于不同范围。本例只在诊断区读取原始通道,帮助验收,不建议业务到处绕过公开 API 自行复制通道协议。

3.3 Example 负责内容区域和计时状态

圆屏布局采用居中的内接正方形。设可用圆形直径为 D,正方形对角线等于 D,所以边长为 D / √2,每侧留白为 (D - D / √2) / 2,约占直径的 14.64%。这里是几何上的保守内容区域,不是系统返回的安全区,也不是对所有手表 UI 的最佳设计结论。

这种布局便于说明适配:正方形四个角位于圆周内,文字和按钮在区域内排布即可。代价也很明确,它没有利用圆屏上下两端的弧形空间。如果页面需要环形刻度、弧形标签或可滚动列表,应用可以采用其他布局;插件只提供形状,不强制这一公式。矩形分支则使用常规边距,不再套用圆屏收缩比例。

以下是本次 Example 的完整布局函数,位于 example/lib/main.dart。它只根据 WatchShape 的形状决定内容区域,原始通道诊断不会改写这个选择:


  Widget build(BuildContext context) {
    return WatchShape(
      builder: (context, shape, _) {
        return Scaffold(
          backgroundColor: const Color(0xFF0A0F18),
          body: LayoutBuilder(
            builder: (context, constraints) {
              final side = math.min(
                constraints.maxWidth,
                constraints.maxHeight,
              );
              // 圆屏按内接正方形留边距,矩形屏沿用应用设定。
              final inset = shape == WearShape.round
                  ? side * ((1 - math.sqrt1_2) / 2)
                  : _rectangularInset;
              return Padding(
                key: const ValueKey('shape-layout'),
                padding: EdgeInsets.all(inset),
                child: _buildTimerContent(),
              );
            },
          ),
        );
      },
    );
  }

诊断、计时和记圈的实现见完整 Example。

计时器使用 Stopwatch 记录经过时间,定时器每隔 100 ms 通知界面刷新。假如某一帧刷新晚了,下一次显示仍读取经过时间,计数不依赖回调执行了多少次。显示保留十分之一秒,页面关闭后这次记录结束;后台训练计时和持久化恢复需要应用另行处理。

记圈时,应用用当前经过时间减去上一次记圈的时间,得到这一圈的用时;日志同时保留累计时间和本圈时间,便于核对。圈数单独累计,列表只保留最近三条,即使旧记录移出列表,下一圈编号也能继续增长。重置后计时重新累计,下一次记圈从第 1 圈开始。

读者可以保留自己的计时和记录方式,只把形状查询接回原来的 WatchShape。

四、接入时容易混淆的条件

4.1 最低 API 与设备声明要一起检查

宿主的 compatibleSdkVersion 需要与原生能力对应。本次两组都按 API 18 声明最低兼容范围,编译使用 API 26,与 screenShape 的引入版本对齐。字段仍然是可选的,所以代码保留缺失值的错误分支;版本符合要求以后,还要看该次查询实际返回了什么。

设备类型是另一项独立配置。插件 HAR 和示例宿主都列入 wearable,宿主同时保留完整的 EntryAbility 和 Flutter 插件注册路径。我的检查顺序是先核对这些声明,再构建、安装和启动。若接入时停在安装阶段,应结合原始错误检查设备类型和签名;若已经进入页面却报通道未实现,则继续检查依赖和注册器,避免把两个阶段的问题混在一起。

本地的工程维护路径带中文,而当前 Hvigor 构建对路径有额外限制。复现说明因此要求把完整源码复制到 ASCII 路径,再清理自动生成缓存、解析依赖和构建。复制的是整个工程,不是只复制一个 .ets 文件;原仓库的许可和必要依赖锁也应保留。把构建机器上的 SDK 绝对路径写进发布包,不能解决工程的可移植性。

4.2 按调用阶段排查日志

原生日志使用应用日志域,记录附着、查询结果和错误。日志是否可见还受设备配置、过滤条件和采集等级影响。查不到 INFO 时,我会先检查过滤条件,再用页面上的原始回执确认调用状态,避免把采集范围误当成运行结果。

我按调用结果选择排查入口:

观察到的结果下一步检查
MissingPluginExceptionwear 通道是否安装了处理器
DISPLAY_READ_FAILED沿错误上下文检查显示查询
读回 round,页面仍排版异常转向 Flutter 布局

表5 排查入口

按阶段拆开以后,插件注册、系统读取和页面显示都有各自的排查入口,日志也无需塞进无关的应用数据。

4.3 常显能力不在本次适配范围

wear_plus 的常显组件和相关状态、恢复设置仍按 Wear OS 机制设计。本次鸿蒙实现只处理形状查询,其他方法返回未实现。

接入时应把支持范围写清楚:鸿蒙侧可以使用本次形状查询,常显组件仍不支持。若页面里原先包着 AmbientMode,需要先把那部分平台调用分开处理。后续扩展常显能力时,还要重新核对鸿蒙公开 API 的生命周期、功耗和设备限制,方法名相似并不代表行为相同。

4.4 默认显示与当前窗口不是同一概念

插件读取默认显示的物理形状。这次在手表的全屏窗口验证这条路径,没有验证外接屏、多显示器或复杂的窗口迁移。如果应用将来出现在不同显示器上,需要重新确认“默认显示”是否就是承载当前窗口的显示器,而不是把本次实现无条件推广到所有桌面场景。

应用还要继续处理窗口约束、文字缩放和布局溢出。本例使用简短标签,在页面上显示最近一次记圈,让内容在有限高度内保持可读。增加长文案或放大字体后,可能需要滚动、重新分组,或者把记录放到另一个页面。这些调整依赖实际内容;形状查询为布局提供依据,应用再决定怎样利用这块空间。

五、结论与可直接复用的建议

5.1 这次适配解决了什么

我补齐了 wear_plus 的鸿蒙形状查询:原来的 Dart 入口不用改,系统形状可以传入 Flutter 布局。圆屏模拟器已收到正确回执,矩形分支通过了主机测试;矩形手表实机尚未验证。计时与记圈是示例场景,库可以供其他穿戴页面复用,社区仓库主页见 oh-flutter/wear_plus。

AtomGit 上 oh-flutter/wear_plus 社区仓库页面与源码目录

图11 社区适配仓库

5.2 在自己的项目中使用

💡 社区仓库的 ohos/wear_plus 分支合入并发版前,可以在应用的 pubspec.yaml 中引用公开适配分支 abaoge/wear_plus(feat/ohos_wear_plus_1.2.5)对应的固定提交,其中包含鸿蒙插件实现与开源合规文档;合入后即可直接切换为社区仓库的 ohos/wear_plus 分支。上游原始标签不含本次适配,不能只按相同版本号替换依赖。

dependencies:
  wear_plus:
    git:
      url: https://atomgit.com/abaoge/wear_plus.git
      ref: c1b0629bd138066cdf8133caed95ca0378f1e5b2

解析依赖后,导入 package:wear_plus/wear_plus.dart,把需要随形状变化的内容放进 WatchShape.builder。第三章的完整布局函数可以直接作为参考:圆屏使用内接区域,矩形屏使用普通边距。保留自己的内容组件即可,无需接入独立探针或整套计时页面。

宿主最低兼容 API 为 18,并声明 wearable 设备类型;本篇使用的编译环境见第二章。首次加入原生插件后需要完整重建应用,单靠 Dart 热重载不能安装新的鸿蒙处理器。若仍报通道未实现,再按第四章检查依赖解析结果和插件注册。

5.3 使用时保留这几个判断

  1. 按形状选择布局。 系统只提供形状,文字、按钮与滚动区域仍由应用安排。内容较多时,不必把所有信息硬塞进一屏。
  2. 按用途解读结果。 普通布局可以沿用上游的圆屏兜底;如果业务必须确认硬件形状,公开接口的默认值不足以作依据,应结合第三章说明的失败语义设计状态提示。
  3. 只使用已支持的能力。 常显接口尚未适配;外接屏和窗口迁移也不在这次验证范围。不要因为形状查询可用,就直接打开包内所有穿戴能力。

中文适配说明整理了接口支持范围与接入条件;需要对照第二章的 1.2.4 实测页面与探针时,可以查看 feat/ohos-watch-shape 分支中的完整 Example和复现 README。日常业务接入使用上面的依赖与组件即可。

小结

对这张计时卡片来说,形状查询接通后,页面有了来自系统的布局依据。应用继续负责内容怎么摆,插件负责把形状读回来,两部分可以分别复用。

参考资料

  1. HarmonyOS 屏幕属性与形状 API
  2. HarmonyOS module.json5 配置
  3. wear_plus 上游源码
  4. oh-flutter/wear_plus 鸿蒙社区仓库

相关推荐

RN 鸿蒙安全三方库落地:react-native-digital-shield,密钥不出 TEE,AES-GCM/RSA-JWT 跨端身份打通

本文基于 RNOH 0.84 与 HarmonyOS API 26,自研数字盾库 react-native-digital-shield,把 ArkTS 专属的 HUKS、asset 能力经TurboModule 桥接给 JS:提供 AES-256-GCM 加解密与密钥托管、敏感资产存储、RSA-2048 签发校验 RS256 JWT,并以 JWK 公钥和 HS256 共享密钥实现跨端身份互认,密钥明文始终不出 TEE。文中还复盘 TurboModule 双侧注册、GCM 需拆分 16 字节

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 8

Flutter 修复鸿蒙 WebView Cookie 同名键丢失:离线回读与开发助手复核实录

修复了 Flutter 三方库 webview_cookie_manager 鸿蒙端因同名键误判导致的 Cookie 名称丢失问题,确保业务键如 path、domain 等在回读时正确保留。修复后,模拟器与真机测试均实现四组数据完整恢复,值中多等号也保持不变。补丁已提交至 CPF-Flutter 官方仓库,提升插件在鸿蒙平台的可靠性。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 10

Flutter 鸿蒙启动图撤销后编译失败:用 DevEco Code 复核三方库修复

本文针对 Flutter 鸿蒙启动图撤销时导致编译失败的问题,修复了 flutter_native_splash_ohos 库在移除启动图时误删仍被引用资源的缺陷。通过保留1×1透明图片并透传自定义模块路径,确保资源引用完整性,使工程可正常构建。修复已提交至官方仓库,支持幂等操作与多场景验证,提升开发体验。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 16

Flutter 接入鸿蒙 ArkWeb 预初始化:帮助页首开时机与主线程成本实测

为解决Flutter鸿蒙版webview_flutter无法在无视图状态下提前初始化引擎的问题,我在其原生实现中新增可选静态入口initializeWebEngine()。实测表明,在2秒预热窗口下,页面首屏渲染(FCP)耗时从中位数1791ms降至1019ms,显著提升首次打开体验;而在零延迟场景下,总路径耗时略有上升,需权衡预热时机与用户等待成本。该补丁已提交至CPF-Flutter官方仓库,支持社区协作优化。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 11

Flutter 优化鸿蒙 printing_ohos 多页 PDF 光栅化:原生资源释放与缓冲复用

针对鸿蒙端 printing_ohos 插件存在的页码映射错误与高内存占用问题,本文提出并实现三项优化:修正页码索引映射、引入原生资源分层释放机制、复用像素缓冲区。修复后,第三页显示正确,内存增长显著降低——在模拟器上PSS增量中位数下降65.8%,真机测试下降64.4%。完整补丁已提交至官方仓库,支持社区协作与问题反馈。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 8

React Native 鸿蒙权限审计实战:让每一项权限都有人认领

本文介绍一款纯 JS 编写的 React Native 鸿蒙权限审计工具,解决权限声明与使用对账难、最小权限集计算矛盾、审核必填字段缺失三大痛点。通过 module.json5 声明、使用台账与源码扫描三路输入对账,自动识别未使用权限、补全 reason/usedScene、生成可粘贴的最小清单,并支持 CI 卡控与风险分评估。工具内核独立,离线运行,兼顾准确性与安全性,助力应用高效通过审核。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 8

Cordova 鸿蒙 WebView 安全实战:能改的运行时拦,改不了的构建期审

本文通过 cordova-plugin-harmony-webguard 插件,解决 Cordova/HarmonyOS 应用中导航安全、引擎不可修改配置、JS 门面静默降级三大难题。基于运行时策略闸门(onPageStart + JS 预检)与构建期审计 CLI(扫描 MainPage.ets、config.xml 等),实现协议拦截、白名单校验、高危配置阻断,支持 CI 失败退出,将安全基线可视化、可测化,真正落地“能改的运行时拦,改不了的构建期审”。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 11

Flutter 鸿蒙供应链审计实战:让高危依赖卡在 CI 门口

本文介绍了一个基于纯 Dart CLI 与 Flutter Dashboard 的依赖审计工具,解决鸿蒙工程中三套锁文件(pubspec.lock、oh-package-lock.json5、package-lock.json)统一读取、离线快照与 OSV 在线情报选择、以及高危依赖通过退出码卡在 CI 的问题。工具以锁文件为事实依据,支持离线快照(4 条真实 GHSA)与在线查询,输出 SBOM(CycloneDX 1.5)、Markdown/JSON 报告,并通过退出码实现 CI 阻断,性能秒级

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 33

React Native 鸿蒙端侧 OCR 实战:TurboModule 与纯 TS 引擎的逐字契约

本文以鸿蒙真机上的 React Native 0.84 工程为例,详解三大难题:通过 TurboModule 接入 HarmonyOS Core Vision Kit 实现端侧离线 OCR;规避 Metro 预置头导致的 require 不存在问题;实现 npm 包与 HAR 双形态交付。项目采用 codegen 生成 ArkTS 接口,结合纯 TS 回退引擎,确保跨引擎逐字符一致,并通过 autolinking 支持无缝集成。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 30

Flutter 鸿蒙端侧 OCR 实战:让文字识别留在设备上

本文通过一个运行在鸿蒙真机上的 Flutter 插件工程,系统解决了三大核心问题:如何统一内存、文件、相册三类输入的识别链路;如何实现与 KMP 版本的 JSON 契约逐字节对齐;以及在系统不提供置信度时如何诚实处理。基于 HarmonyOS Core Vision Kit,插件实现端侧离线 OCR,全程无网络、无权限、无密钥,支持真实小票与截图识别,性能稳定(312~507ms),并通过 35 项单元测试与真机集成验证。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 49

跨端 RN 鸿蒙碰一碰分享库实战:基于 Share Kit 封装 react-native-ohos-knock-share,实现卡片、文件一碰传

本文介绍基于React Native的鸿蒙碰一碰分享库,解决RN开发者在鸿蒙生态中无法便捷使用Share Kit碰一碰功能的痛点。通过封装NFC/星闪触碰、3秒发送窗口、沙箱文件URI转换等核心逻辑,实现跨设备快速传递链接、文件、图片等内容。支持手机、平板、PC多端适配,提供回环演示与真机实测验证,显著降低接入门槛,提升跨端协作效率。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 37

React Native 鸿蒙实战:给应用接入小艺语音指令,跑通 Skill 声明到操作闭环

本文介绍如何在 React Native 鸿蒙(RNOH)工程中实现小艺技能闭环,通过开源库 react-native-ohos-ai-agent 封装技能声明、语音指令接收、端侧执行与结果回传全流程。针对三方应用权限限制,精准定位可操作边界,实现“被唤起→执行→反馈”链路贯通。结合五张真机截图验证:技能注册、指令匹配、意图上报、智能体调起及对话层弹出等关键节点均成功,完整打通语音交互闭环,助力应用无需入口即可被用户“叫”到。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 44

Flutter 鸿蒙实现精准碰一碰分享:tap_share_ohos 三方库打通真机传文件与传卡片

本文针对Flutter在鸿蒙系统上“碰一碰”分享功能缺失的痛点,基于DevEco Studio 26.0、HarmonyOS 7.0 API 26与Flutter 3.41.9实测环境,提出并实现开源库tap_share_ohos。该库封装NFC、Share Kit、权限管理及分布式能力,通过Dart门面层统一接口,利用MethodChannel与BasicMessageChannel对接原生ArkTS逻辑,实现文件与卡片的碰一碰分享。真机测试显示,可无缝调起系统分享面板,支持华为分享接力

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 126

Flutter 鸿蒙实战:用三方库接入小艺语音指令与智能体,跑通 Skill 声明到操作闭环

本文基于 flutter_ohos_ai_agent 三方库,实现 Flutter 应用一键调起鸿蒙小艺智能体。针对跨端框架对接鸿蒙 AI 能力的碎片化、环境差异等痛点,通过插件封装系统 Kit 能力,提供统一 Dart API。实测显示,模拟器与真机均支持文本/语音指令闭环,真机可完整调起小艺面板并回传事件。核心原理为:利用 ArkUI 承载页桥接 FunctionComponent,通过 MethodChannel 与 EventChannel 实现 Flutter 与原生能力双向通信,完成从语音识别到

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 41

RNOH 原生内存泄漏定位与治理:借助 LeakyGuard 实现图片解码降采样、LRU 字节缓存、资源生命周期管控,长列表图片页面峰值内存优化

本文面向 RNOH 0.84 长列表图片场景,剖析滑动数分钟后进程被系统杀掉的根因:原图解码尺寸失控、缓存按条数限流、PixelMap未显式 release。基于自研LeakyGuar,TurboModule 库,给出 desiredSize 真降采样、字节预算 LRU 缓存(逐出必 release)、资源登记与快照 diff、卸载即释放 Hook 的闭环方案,实测单图内存降 98.6%,24 图峰值由 1098.6MB 降至15.8MB。并总结双侧注册、ArkTS 语法差异、vp 转像

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 105

Flutter 鸿蒙空间音频实战:让音乐播放器的声音“长出方位感“

本文通过一个真实运行的 Flutter 工程,系统解析了在鸿蒙系统上实现空间音频的三大核心问题:基于双耳时间差(ITD)、声级差(ILD)和频谱整形的方位感算法;耳机切换与音频焦点管理;以及播放器与音效的集成。项目使用自研插件 spatial_audio,结合原生 HarmonyOS 接口,实现了跨设备的空间音频体验,支持普通耳机,无需硬件依赖,并提供实时参数反馈与系统状态监听。附带完整代码仓库与实测效果展示,为 Flutter on HarmonyOS 的音频开发提供了可复用的技术方案。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 272

Flutter 鸿蒙 HiTrace 请求归因:图片压缩等待时,主线程在做什么

为定位鸿蒙平台图片压缩性能瓶颈,我在 flutter_native_image 插件中新增可选的 HiTrace 阶段追踪功能,支持按请求开启解码、缩放、编码与资源释放阶段的耗时分析。通过对比模拟器与真机(华为畅享 100 Pro Max)数据,精准识别出耗时主要集中在解码与编码阶段,显著提升诊断效率。相关代码已提交至官方仓库,助力开发者快速定位性能问题。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 适配鸿蒙折叠屏铰链角度按需订阅:支架调节页用完即停与重入实测

本文介绍对 Flutter 三方库 dual_screen 的优化:针对支架调节页等短时观察场景,保留原有缓存入口的同时,新增按需订阅机制。通过按需启停监听,实现页面退出后停止原生回调,避免无效数据更新。实测显示,无消费者时原生回调从3条降至0条,重进页面则等待新事件,更符合临时工具页的使用逻辑。完整代码已开源,支持鸿蒙折叠屏角度动态感知,适用于需精准控制生命周期的折叠屏应用开发。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 优化鸿蒙剪贴板大图复制卡顿:从主线程同步阻塞到异步文件描述符直解

针对鸿蒙系统下 flutter_image_clipboard 插件复制高清图片时阻塞主线程的问题,本文提出三项优化:异步文件描述符直解、异步剪贴板写入及原生资源逆序释放。实测显示,模拟器事件循环最大间隔从300.32ms降至54.36ms(降幅81.9%),真机上更从133.20ms降至11.20ms(降幅91.59%),显著提升页面响应性。完整代码已开源,支持直接使用与社区交流。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Flutter 三方库 petalmaps_ohos 的 OpenHarmony 适配实战(0 → 1 全记录)

本文详细记录了从零构建 Flutter 三方库 petalmaps_ohos 并适配 OpenHarmony/HarmonyOS 平台的全过程,涵盖项目初始化、原生实现(基于 ArkTS 和 MethodCallHandler)、插件注册、功能验证及升级至 HarmonyOS SDK 26 的关键步骤。重点解析了为何需实现 AbilityAware 获取 UIAbilityContext 以确保地图调起成功,并通过分层架构、枚举封装与兼容性设计,实现功能完整、易用且低维护成本的“调起系”地图插件,

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 52

Flutter 三方库 holding 的 OpenHarmony 适配实战:从 0 到 1 开发一个鸿蒙感知插件

本文详述了从零实现开源 Flutter 插件 holding 并适配 OpenHarmony/HarmonyOS 的全过程。基于鸿蒙专属 MultimodalAwarenessKit,实现握持手与操作手状态感知,覆盖事件流订阅、状态查询及 API 26 新增接口,确保功能一致、性能高效、接口稳定。通过 Dart 层封装、ArkTS 原生桥接、真机验证与单元测试,完成插件工程化落地,并发布至 pub.dev,为同类系统能力插件的鸿蒙化提供可复用的技术路径。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 25

Flutter 三方库 gesturesshare 从 0 到 1:鸿蒙隔空传送分享插件的开发、升级与发布实战

本文详述了 Flutter for OpenHarmony 三方库 gesturesshare 从零开发到发布全过程:基于 HarmonyOS 隔空传送能力,实现跨设备图片与 App Linking 一键分享。涵盖双端代码设计、API 26 升级、真机验证及 Pub 发布流程,提供 Dart/ArkTS 对照与踩坑复盘。项目填补 Flutter 生态空白,推动鸿蒙多设备交互能力普及,助力开发者快速接入系统级功能。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 26

Qt 鸿蒙沉浸光感实战:让玻璃的每个像素自己发光

本文通过一个 QML 模块与 Qt for OpenHarmony 构建实践,完整解答了三个核心问题:如何用 81 行 GLSL 实现无图片依赖的动态玻璃材质(含光核漂移、视差、SDF 圆角与颗粒);如何在 Qt Quick 中通过 C++ 控制器实现弹层锚定与翻转;以及如何将 Qt 应用交叉编译并以 HAR 形式部署至鸿蒙系统。项目基于 Qt 5.12.12,利用 ShaderEffect 实现纯渲染级视觉效果,兼顾性能与跨平台兼容性,提供可复用的开源方案。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 21

开源鸿蒙平台 KMP_CMP 三方库「端侧语音转文字」适配全流程

本项目实现KMP/CMP在OpenHarmony鸿蒙平台的语音转文字(TTS)完整链路适配,通过Kotlin/Native共享模型、C ABI/N-API桥接与ArkTS交互,解决权限、音频格式、生命周期及跨平台契约等难题。支持中文、英文、日文多语言识别,提供统一结果模型与JSON序列化,实测于华为Mate 80 Pro Max模拟器,验证了JVM测试、Native动态库、HAP签名部署及真机识别全流程。项目结构清晰,分六阶段推进,确保代码复用与平台无关性,为KMP在鸿蒙生态落地提供可复用的技术范式。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 30

开源鸿蒙平台 KMP_CMP 三方库「端侧OCR」新特性适配

本项目实现Kotlin Multiplatform(KMP)在OpenHarmony平台的端侧OCR适配,基于HarmonyOS 7.0 SDK与Compose Multiplatform 1.9.2,成功打通JVM、Kotlin/Native与ArkTS间的数据交互。通过C ABI/N-API桥接层,实现KMP模型与Core Vision Kit识别结果的跨语言共享,支持中、英、日、韩四语识别。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 11

开源鸿蒙平台 KMP/CMP 三方库「智感握姿」新特性

本项目实现鸿蒙KMP/CMP平台下智感握姿能力的跨端适配,基于Kotlin Multiplatform构建共享状态模型,通过C ABI与ArkTS桥接,支持左手、右手、双手等五种握持状态的稳定传递。项目打通ohosArm64目标、Native动态库构建与DevEco Studio集成链路,提供统一API与真机验证示例,确保状态模型、事件序列与生命周期在多端一致。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 16

Electron 鸿蒙沉浸光感实战:把玻璃交给操作系统去发光

本文通过一个 Electron 库与 HarmonyOS 三方库,系统解决三大核心问题:跨平台材质路径对齐(macOS vibrancy、Windows acrylic、鸿蒙 backgroundBlurStyle)、菜单子窗口精准定位(坐标转换与工作区适配)、以及无脚本菜单文档的安全事件回传(自定义 URL scheme 与焦点管理)。基于 CPF-Electron 鸿蒙化分支,实现桌面级玻璃效果——材质由操作系统合成器采样,网页仅渲染薄色层与光斑,确保视觉一致性与性能优化。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 21

Flutter 修复 flutter_secure_storage 鸿蒙入口路由与前缀隔离:让多账号登录凭证在手机上独立读写与清理

本文针对 flutter_secure_storage 在鸿蒙平台的适配问题,修复了主包缺失 TargetPlatform.ohos 路由导致的 unsupported_platform 错误,并统一鸿蒙子包多账号前缀的读写与清理范围,解决键值覆盖、前缀错用及批量操作匹配过宽等问题。修复后,业务层可继续使用统一接口实现跨账号安全存储,保障数据隔离性。完整代码已开源至 abaoge/flutter_secure_storage。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 1万+

Cordova 鸿蒙沉浸光感实战:让弹层玻璃“喝到“背景的光

本文通过一个 HarmonyOS 模拟器上的 Cordova 项目,详解了在 WebView 中实现“玻璃拟态”效果的三大核心问题:利用 backdrop-filter 实现背景实时模糊与提亮,构建四层结构(采样、渐变底、光场、噪点)模拟真实玻璃质感;通过锚点定位与动态布局实现弹层贴边生长;并集成焦点管理、系统返回键及 inert 属性,确保交互礼仪完整。插件为纯 JS 无原生依赖,基于 ArkWeb 内核,兼顾性能与真实背景采样,适用于菜单、浮层等场景。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 25

CMP 鸿蒙透明玻璃实战:让悬浮导航栏真正透出光场

本文通过一个KMP/CMP工程,详解如何在鸿蒙上用Kotlin实现沉浸光感玻璃材质的逐像素渲染。核心解决三大问题:透明感源于环境光场与带alpha的柔光混合、玻璃“透光”依赖alpha保留的屏幕混合算法、跨平台一致性通过软件光栅器与原生桥接实现。项目以纯代码生成全屏光场与悬浮玻璃组件,无图片资源,支持动态切换调色板,实测模拟器下亚秒级重绘,完整复现官方“沉浸光感”视觉效果。仓库地址:https://atomgit.com/lqjmac/spatial-glass-cmp。

白天写代码,晚上追AI大模型,偶尔搞点八卦 开源动态|技术干货|实习机会|生态活动 我负责分享,你负责变强 20
上一篇: Flutter 优化鸿蒙剪贴板大图复制卡顿:从主线程同步阻塞到异步文件描述符直解
下一篇: Flutter 适配鸿蒙折叠屏铰链角度按需订阅:支架调节页用完即停与重入实测
AtomGit 代码君
AtomGit 代码君 企业官方账号 企业官方账号
博客等级 码龄6年 2万+粉丝 856原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值