Flutter 鸿蒙 clipboard_watcher 0.3.0 使用实战:监听剪贴板变化并管理生命周期
本文用
clipboard_watcher 0.3.0在 Flutter
鸿蒙页面监听“剪贴板已变化”事件,覆盖启动、停止、重新订阅、错误处理和页面销毁。插件只通知变化,不读取或上传剪贴板内容。三方库仓库: https://atomgit.com/oh-flutter/clipboard_watcher
本文锁定版本:
bfd2b4fc14c34b6d93a19e5997e40ef1abbeb7eb完整 Demo: clipboard_watcher/example(受测提交)
一、最终效果

图 1:CHZ-AL00 / HarmonyOS 7.0.0.105 上完成 start、stop 和 restart,事件数按 1 -> 1 -> 2 变化。



| 场景 | 实测结果 |
|---|---|
| 重复启动 | 一次系统更新只触发一次回调 |
| 停止监听 | 停止期间更新剪贴板,计数保持不变 |
| 重新启动 | 新事件恢复投递,最终累计 2 次 |
| 自动化 | 3 Dart + 1 Widget + 6 ArkTS,共 10 项通过 |
| 隐私边界 | 事件不包含剪贴板文本 |
二、为什么只监听变化
验证码自动填充、粘贴按钮提示和跨应用复制提醒通常先需要知道“内容变了”,再由用户动作决定是否读取。把变化监听和内容读取拆开,可以减少不必要的数据访问,也让页面生命周期更清楚。
clipboard_watcher 使用单例 clipboardWatcher,业务对象实现 ClipboardListener。start() 控制原生监听,addListener() 只控制 Dart 回调列表,两者不是同一个动作。遗漏任意一边都可能造成“原生在监听但页面收不到”或“页面已销毁仍留着 listener”。
三、环境和依赖
本文实测 Flutter OH 3.41.10-ohos-1.0.1、Dart 3.11.5、DevEco Studio 26.0.0 Release、API 26 与 CHZ-AL00。预览标签 3.44.9+ohos-0.0.1-canary1 未用于本库回归。
dependencies:
clipboard_watcher:
git:
url: https://atomgit.com/oh-flutter/clipboard_watcher.git
ref: bfd2b4fc14c34b6d93a19e5997e40ef1abbeb7eb
flutter pub get
当前没有 OHOS 稳定 TAG,所以使用真机受测代码 SHA。远程分支 HEAD 后续只增加了设备验证文档,不应直接用漂移分支替代锁定值。

图 2:AtomGit 适配分支和当前 HEAD;业务依赖仍锁定真机代码提交。
该插件通过系统 pasteboard 变化事件工作,不读取内容,也不需要新增权限。若业务在回调中主动调用 Clipboard.getData(),那是业务自己的读取行为,应单独说明用途、隐私提示和数据保留策略。
四、正确的初始化与释放顺序
import 'package:clipboard_watcher/clipboard_watcher.dart';
import 'package:flutter/material.dart';
class ClipboardPageState extends State<ClipboardPage>
with ClipboardListener {
int _changes = 0;
bool _listening = false;
String? _error;
void initState() {
super.initState();
clipboardWatcher.addListener(this);
_start();
}
Future<void> _start() async {
try {
await clipboardWatcher.start();
if (mounted) setState(() => _listening = true);
} catch (error) {
if (mounted) setState(() => _error = error.toString());
}
}
void onClipboardChanged() {
if (mounted) setState(() => _changes++);
}
Future<void> _stop() async {
await clipboardWatcher.stop();
if (mounted) setState(() => _listening = false);
}
void dispose() {
clipboardWatcher.removeListener(this);
super.dispose();
}
}
如果这个页面是应用中唯一的监听方,离开前还应在可等待的路由退出流程调用 stop()。不要在同步 dispose() 里无条件停止全局 watcher,因为其他页面可能仍依赖同一个单例。更稳妥的架构是由一个应用级服务统一 start/stop,再把事件分发给页面。
五、暂停与恢复按钮
Future<void> toggleClipboardWatcher() async {
try {
if (_listening) {
await clipboardWatcher.stop();
} else {
await clipboardWatcher.start();
}
if (mounted) setState(() => _listening = !_listening);
} catch (error) {
if (mounted) setState(() => _error = error.toString());
}
}
按钮要在 Future 完成期间禁用,避免快速点击使 start/stop 顺序交叉。插件原生端会对重复 start 做幂等处理,并用 generation 阻止取消后的旧回调进入新会话,但 UI 仍应保持清晰的单一状态。

图 3:OHOS pasteboard update 订阅、取消与旧回调隔离;没有读取文本。
六、测试、构建与真机验证
flutter analyze
flutter test
node --test ohos/test/clipboard_watcher_lifecycle.test.cjs
cd example
flutter test
flutter build hap --debug --no-codesign

图 4:10 项 Dart、Widget 与 ArkTS 生命周期用例通过。

图 5:HAP 构建结果以及真机宿主锁定的提交。

图 6:真实系统剪贴板更新、停止期间无事件和重订阅后的事件计数。
真机流程是:start 后制造一次系统剪贴板变化,计数到 1;stop 后再次变化,计数仍为 1;重新 start 后变化,计数到 2。后台长期运行、Engine 重建和多页面共同管理同一单例尚未覆盖。
七、常见问题
Q1:一次复制触发多次回调
- 现象: 同一页面多次进入后,计数成倍增加。
- 原因: 重复
addListener(this),离开时没有removeListener,或业务额外创建了自己的转发订阅。 - 解决方法: 在成对生命周期里注册/移除,由单一服务管理全局 start/stop。
- 验证结果: 自动化和真机重复 start 均保持单次投递。
Q2:stop 后仍处理旧事件
- 现象: 停止附近的异步回调晚到。
- 原因: 系统事件与取消动作可能同时在队列中。
- 解决方法: 使用受测提交的 generation 防护,业务回调也检查当前
_listening和mounted。 - 验证结果: 真机停止期间事件数保持 1,重订阅后才增加。
Q3:为什么回调里没有文本
- 现象:
onClipboardChanged()没有参数。 - 原因: 本库契约只通知变化,刻意不读取内容。
- 解决方法: 只有明确业务需求和合适用户动作时,另用 Flutter Clipboard API 读取。
- 验证结果: ArkTS 源码和真机日志都不包含剪贴板内容。
八、总结
clipboard_watcher 适合把剪贴板变化当作轻量信号。可靠接入的关键是固定受测 SHA、正确区分 Dart listener 与原生 watcher、成对管理生命周期,并把内容读取留给明确的业务动作。本文已验证前台 start、stop 和重订阅,后台与复杂多页面场景仍需项目自行补测。
九、参考链接
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐


所有评论(0)