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

适配分支: feat/ohos_clipboard_watcher_0.3.0

一、最终效果与适配目标

验证码填充、口令导入、跨应用复制提示等功能,通常不需要持续读取剪贴板内容,只需要知道“剪贴板刚刚发生了变化”。clipboard_watcher 0.3.0 已经把这件事抽象成 start()stop()ClipboardListener,并覆盖 Android、iOS 和桌面平台,但原仓库没有 OHOS 实现。

我这次保留 Dart API 和 clipboard_watcher 通道不变,只在 ArkTS 侧订阅系统剪贴板的 update 事件。插件不会读取、记录或上传剪贴板内容,因此实现范围比“读取剪贴板”更小,也不应该在文章里把它写成内容访问能力。

在这里插入图片描述

图 1:联合验证宿主在真机上显示剪贴板启停检查结果。

验证点实测结果证据
HAP 构建、签名、安装和启动通过图 1、图 5
重复 start()一次剪贴板更新只收到一个事件图 6
stop()停止期间更新剪贴板,计数不增加图 6
重新 start()事件恢复,最终累计为 2图 6
隐私边界实现不读取、记录或上传剪贴板内容图 3、图 6

成果速览

项目内容
上游基线TAG v0.3.0,提交 8d764c454fcf3f78e116ec39514fbc1464bb4865,MIT
适配分支feat/ohos_clipboard_watcher_0.3.0
适配 TAG尚未发布
真机受测提交bfd2b4fc14c34b6d93a19e5997e40ef1abbeb7eb
当前分支 HEADe8c32e033a53477db636e9a95ced5e67134a34ac,后续仅补真机记录
自动化验证3 项 Dart、1 项 Widget、6 项 ArkTS,共 10 项通过
真机结论启动、停止和重订阅通过;后台长期运行与 Engine 重建未覆盖

二、本次实测环境

项目实测版本
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
目标库clipboard_watcher 0.3.0

Flutter OH 安装过程见 环境搭建指南。截至 2026 年 9 月 12 日,版本号最大的标签是预览版 3.44.9+ohos-0.0.1-canary1,最新正式稳定标签仍是 3.41.10-ohos-1.0.1,也是本文完成构建和真机回归的版本。

三、从上游基线到 AtomGit 分支

适配前已核对三方库清单和 oh-flutterCPF-Flutterhxa-flutter 当日仓库列表,在当时公开搜索范围内没有发现同名 OHOS 实现。上游基线为 v0.3.0,提交 8d764c454fcf3f78e116ec39514fbc1464bb4865,MIT 许可证和完整历史都保留在 AtomGit 仓库中。

git clone https://atomgit.com/oh-flutter/clipboard_watcher.git
cd clipboard_watcher
git switch feat/ohos_clipboard_watcher_0.3.0
git remote -v

分支从上述标签提交创建。干净副本补 OHOS 骨架时使用 Flutter 自带插件模板:

git switch -c feat/ohos_clipboard_watcher_0.3.0 8d764c454fcf3f78e116ec39514fbc1464bb4865
flutter create --template=plugin --platforms=ohos --no-pub .

生成后再按原插件命名调整 pubspec.yaml、HAR 入口和示例,而不是直接保留模板通道。图 2 汇总了实际 AtomGit origin、适配分支和 HEAD,可核对仓库来源与当前分支状态。

在这里插入图片描述

图 2:AtomGit origin、统一命名分支与当前 HEAD。

四、原项目的调用链

Dart 层有一个单例 ClipboardWatcher。调用 start()stop() 时,它通过 MethodChannel('clipboard_watcher') 通知原生端;收到 onClipboardChanged 回调后,再逐个通知本地 listener。

class ClipboardObserver with ClipboardListener {
  
  void onClipboardChanged() {
    // 这里只更新界面计数,不读取剪贴板正文。
  }
}

final observer = ClipboardObserver();
clipboardWatcher.addListener(observer);
await clipboardWatcher.start();

因此 OHOS 端要保持三个名字完全一致:startstop 和回调 onClipboardChanged。上层接口不需要为鸿蒙增加分支。

五、用 pasteboard 订阅系统更新

pubspec.yaml 新增入口:

flutter:
  plugin:
    platforms:
      ohos:
        pluginClass: ClipboardWatcherPlugin

核心实现位于 ohos/src/main/ets/ClipboardWatcherPlugin.ets,使用 @kit.BasicServicesKit 的系统 pasteboard:

const board = pasteboard.getSystemPasteboard();
const generation = ++this.generation;
const callback = (): void => {
  if (this.listening && generation === this.generation) {
    this.channel?.invokeMethod('onClipboardChanged', null);
  }
};
board.on('update', callback);

这里没有调用获取内容的 API。start() 重复执行时直接返回,避免一个 Flutter listener 对应多个原生订阅。stop() 用同一个 board 和 callback 执行 off('update'),不能使用“清空该事件全部监听者”的做法,否则可能误伤宿主里其他组件。

generation 用来解决排队回调问题。停止、重新订阅或引擎分离都会递增代次;旧事件即使已经进入队列,真正执行时也会被挡掉。引擎解绑还会移除 MethodChannel handler 并清理自身订阅,避免热重启后重复通知。

在这里插入图片描述

图 3:pasteboard on/off 与 generation 防旧回调的实际实现。

六、交付文件怎么补

本次保留原 LICENSEREADME.mdCHANGELOG.md,新增双语 README.OpenHarmony.mdREADME.OpenHarmony_CN.md、OHOS HAR、example/ohos/、Dart 通道测试、示例 widget 测试和原生生命周期测试。目标仓库当前没有 README.OpenSource,所以没有为了凑模板创建空文件;如果后续社区准入规则明确要求,再按规则补齐。

示例不展示或打印剪贴板内容,只提供开始、停止、复制示例文本和变化计数。这样既能验证事件,又不会在演示代码中形成不必要的数据暴露。

七、静态检查、测试与 HAP

flutter pub get
flutter analyze
flutter test
node --test ohos/test/clipboard_watcher_lifecycle.test.cjs
cd example
flutter test
flutter build hap --debug --no-codesign

实际结果是静态分析无问题,3 项 Dart 通道测试、1 项 widget 测试和 6 项执行生产 ArkTS 源码的生命周期测试通过,共 10 项功能测试。无签名 HAP 构建成功,产物为 example/build/ohos/hap/entry-default-unsigned.hap。Node 用例只替换 Flutter 和系统边界,不重新抄一份业务逻辑。

远程 Git 依赖也在隔离宿主中完成了解析和签名 HAP 构建,受测代码提交为 bfd2b4fc14c34b6d93a19e5997e40ef1abbeb7eb。后续 e8c32e0 仅补真机记录,不能冒充重新构建过的代码 SHA。

在这里插入图片描述

图 4:Flutter 复跑结果与 Dart/ArkTS 完整用例统计。

在这里插入图片描述

图 5:实际 HAP 产物摘要及宿主锁定的 AtomGit 提交。

八、Demo 接入与真机验证

dependencies:
  clipboard_watcher:
    git:
      url: https://atomgit.com/oh-flutter/clipboard_watcher.git
      ref: bfd2b4fc14c34b6d93a19e5997e40ef1abbeb7eb

执行 flutter pub get 后应确认 pubspec.lockresolved-ref 与上述 SHA 一致。分支当前包含后续文档提交,不应用漂移分支替代真机受测代码。

真机在同一宿主进程中完成三段检查:重复调用 start() 后修改剪贴板,只收到一次事件;调用 stop() 后再次修改,计数没有增加;重新调用 start() 后修改,事件恢复,最终累计为 2。未知 MethodChannel 方法返回 notImplemented

这组结果证明了真实系统事件、幂等启动、停止和重订阅,不等于已经覆盖后台长期运行、引擎重建及全部系统策略。验证过程没有读取剪贴板内容。仓库内 integration_test 和 Hypium 也没有执行,不能写成已通过。

在这里插入图片描述

图 6:真机 start/stop/restart 的事件计数和隐私边界。

九、提交适配分支

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 clipboard_watcher"
git push -u origin feat/ohos_clipboard_watcher_0.3.0

截至 2026 年 9 月 12 日,仓库可匿名读取,默认分支就是适配分支,远端 HEAD 为 e8c32e033a53477db636e9a95ced5e67134a34ac。推送前检查未包含签名、证书、本机路径、Node 依赖和构建产物。

十、FAQ

Q1:重复 start 后一次复制触发多次回调

  • 现象: 计数一次增加两次或更多。
  • 原因: 原生端重复注册 pasteboard.on('update')
  • 解决方法:listening 保证启动幂等,并保存自己的 callback 用于注销。
  • 验证结果: 真机重复启动后只收到一次事件。

Q2:stop 后仍收到一次旧事件

  • 现象: 停止监听后,排队中的 callback 仍进入 Flutter。
  • 原因: 系统事件已排队,单纯调用 off 不一定能撤回它。
  • 解决方法: 在 callback 中比较订阅代次和当前 listening 状态。
  • 验证结果: 自动化覆盖旧回调隔离;真机停止期间计数保持不变。

Q3:为什么事件没有剪贴板文本

  • 现象: 回调参数为 null
  • 原因: 这个插件的职责是监听变化,不是读取内容。
  • 解决方法: 业务确需读取时另行遵守系统权限和隐私规则,不要偷偷扩张本插件能力。
  • 验证结果: 当前实现不申请内容读取权限,也不输出剪贴板值。

十一、总结

clipboard_watcher 的 OHOS 适配保持原 Dart API 不变,用系统 pasteboard 更新事件补齐了开始、停止和回调链路。代次校验解决了旧事件串入新订阅的问题,10 项自动化、HAP 构建和真机启停回环都有独立证据。能力边界同样明确:它只报告变化,不读取内容,也不保证后台策略之外的持续投递。

十二、参考链接

欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter

Logo

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

更多推荐