Flutter 鸿蒙插件适配实战:用 clipboard_watcher 0.3.0 监听剪贴板变化
适配仓库: 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 |
| 当前分支 HEAD | e8c32e033a53477db636e9a95ced5e67134a34ac,后续仅补真机记录 |
| 自动化验证 | 3 项 Dart、1 项 Widget、6 项 ArkTS,共 10 项通过 |
| 真机结论 | 启动、停止和重订阅通过;后台长期运行与 Engine 重建未覆盖 |
二、本次实测环境
| 项目 | 实测版本 |
|---|---|
| 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 |
| 目标库 | 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-flutter、CPF-Flutter、hxa-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 端要保持三个名字完全一致:start、stop 和回调 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 防旧回调的实际实现。
六、交付文件怎么补
本次保留原 LICENSE、README.md 和 CHANGELOG.md,新增双语 README.OpenHarmony.md、README.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.lock 的 resolved-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
更多推荐



所有评论(0)