本文用 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,业务对象实现 ClipboardListenerstart() 控制原生监听,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 防护,业务回调也检查当前 _listeningmounted
  • 验证结果: 真机停止期间事件数保持 1,重订阅后才增加。

Q3:为什么回调里没有文本

  • 现象: onClipboardChanged() 没有参数。
  • 原因: 本库契约只通知变化,刻意不读取内容。
  • 解决方法: 只有明确业务需求和合适用户动作时,另用 Flutter Clipboard API 读取。
  • 验证结果: ArkTS 源码和真机日志都不包含剪贴板内容。

八、总结

clipboard_watcher 适合把剪贴板变化当作轻量信号。可靠接入的关键是固定受测 SHA、正确区分 Dart listener 与原生 watcher、成对管理生命周期,并把内容读取留给明确的业务动作。本文已验证前台 start、stop 和重订阅,后台与复杂多页面场景仍需项目自行补测。

九、参考链接

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐