Flutter 鸿蒙插件适配实战:flutter_screenshot_detect 0.1.7 截图事件监听

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

适配分支: feat/ohos_flutter_screenshot_detect_0.1.7

一、最终效果与适配目标

聊天阅后即焚、票据展示、内容创作工具常在用户截图后给出提示或埋点。flutter_screenshot_detect 0.1.7 的职责是发送"当前应用窗口发生截图"的事件,不是阻止截图,也不是读取相册,更不检测录屏。原库已经有 Android 和 iOS 代码,OHOS 平台缺失。

我保留 onScreenshot 事件流和 startListening() 便捷接口,底层接入鸿蒙窗口的 screenshot 事件。最终 Flutter 收到 method、timestamp 和 path 三个字段,其中 OHOS 公共窗口回调不提供保存路径,所以 path 必须是 null,不能伪造一个文件地址。下面两张都是同一台鸿蒙真机上的 Flutter 应用页面,不是其他平台或排版模拟图。

图 1:应用刚启动时计数为 0,状态为 Listening,说明页面已建立事件订阅。

图 2:真实截图后计数变为 1,页面显示 method=ohos_window_screenshot 和实际时间戳。

验证点 实测结果 证据
HAP 构建、签名、安装和启动 通过 图 1、图 6
订阅期间截图 收到事件,计数增加 图 2、图 9
取消订阅 再次截图时计数保持不变 图 7、图 8
重新订阅 事件投递恢复,计数变为 2 图 9、图 10
OHOS 截图路径 path=null,系统窗口回调不提供路径 图 10

二、成果速览

项目 内容
库名称与版本 flutter_screenshot_detect 0.1.7
上游基线 发布版 0.1.7,提交 5667a5212fae9013ac2b5a15b038aef0e19c6b37,MIT
适配分支 feat/ohos_flutter_screenshot_detect_0.1.7
适配 TAG 尚未发布
真机受测提交 1da71f4294faa3fc2584c720e3d75df770893236
当前分支 HEAD 609b0793a6f0a6917cc4a4b6f7ec717fbd141bea,后续两次提交只补验证文档
自动化验证 10 项 Dart、1 项 Widget、8 项 ArkTS,共 19 项通过
真机边界 组合键与系统抓图触发通过;多实例、Ability 重建和其他系统版本未覆盖

三、实测环境

项目 版本
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
目标库 flutter_screenshot_detect 0.1.7

环境安装见 Flutter OH 环境搭建指南

截至 2026 年 9 月 12 日,版本号最大的 Flutter OH 标签是 3.44.9+ohos-0.0.1-canary1,但它仍是预览版;最新正式稳定标签是 3.41.10-ohos-1.0.1。本文所有构建与真机证据都来自上表稳定环境,不把未完成同等回归的 SDK 写成实测版本。

四、上游代码如何进入 AtomGit

发布基线为 0.1.7,Git 基线提交 5667a5212fae9013ac2b5a15b038aef0e19c6b37。适配前已比对发布包的主要源码、清单和 MIT 许可证,并检查三方库清单及组织仓库,当时没有同名 OHOS 实现。

shell 复制代码
git clone https://atomgit.com/oh-flutter/flutter_screenshot_detect.git
cd flutter_screenshot_detect
git switch feat/ohos_flutter_screenshot_detect_0.1.7
git branch --show-current

基线复现命令:

shell 复制代码
git switch -c feat/ohos_flutter_screenshot_detect_0.1.7 5667a5212fae9013ac2b5a15b038aef0e19c6b37
flutter create --template=plugin --platforms=ohos --no-pub .

Flutter 模板只提供插件壳,真正要迁移的是原事件通道名称、事件结构和订阅生命周期。图 3 集中保留了 AtomGit origin、分支、HEAD 和工作区状态,用来核对后续代码分析的真实来源。

图 3:AtomGit 来源、适配分支与当前 HEAD。

五、Dart 端为什么要共享底层流

原接口通过 EventChannel('com.ss.detect/events') 接收事件。适配期间还修正了多实例问题:同一个 binary channel 只能有一个消息处理器,如果每个 detector 都独立创建原生流,其中一个实例释放时可能中断其他实例。最终 Dart 实现让多个对象共享底层广播流,各自只管理自己的订阅。

dart 复制代码
final detector = FlutterScreenshotDetect();
final subscription = detector.onScreenshot.listen((event) {
  print('${event.method}: ${event.timestamp}');
});

// 页面释放时
await subscription.cancel();
detector.dispose();

直接订阅 onScreenshot 时由调用方取消;startListening(callback) 内部持有的订阅则由 dispose() 取消。释放后同一实例仍可重新使用。

六、OHOS 窗口事件实现

pubspec.yaml 注册 FlutterScreenshotDetectPlugin。插件同时感知 Engine 和 Ability 生命周期,因为获取当前窗口需要 UIAbility context:

typescript 复制代码
const currentWindow = await window.getLastWindow(context);
const callback = (): void => {
  if (generation !== this.generation || events !== this.events) {
    return;
  }
  const event = new Map<string, string | number | null>();
  event.set('method', 'ohos_window_screenshot');
  event.set('timestamp', Date.now() * 1000);
  event.set('path', null);
  events.success(event);
};
currentWindow.on('screenshot', callback);

Dart 接口使用微秒时间戳,所以把系统毫秒值乘以 1000。实际精度仍是毫秒,不能因为单位变成微秒就声称精度更高。

窗口查询是异步的。查询期间可能发生取消订阅、Ability 重挂或 Engine 分离,因此注册前必须比较 generation、EventSink 和 context。停止时调用同一窗口的 off('screenshot', callback);窗口已经销毁导致注销失败时记录日志,同时依靠代次阻止旧 callback 继续投递。

该 API 从 API 9 可用,不需要相册权限。插件只监听宿主当前窗口,不看其他应用,不读取截图内容。

图 4:当前窗口 screenshot 事件、EventSink 和 generation 校验。

七、仓库交付内容

适配分支保留上游历史、MIT LICENSE、Android/iOS 实现,新增 OHOS HAR、平台声明、example/ohos/、双语 OpenHarmony 文档、Dart 通道测试、示例测试和 ArkTS 生命周期回归。示例提供开始、暂停、重启监听、事件计数和最近事件信息,方便观察取消是否生效。

不提交 HAP、签名材料、SDK 路径和 Node 依赖。公共窗口事件不需要权限,文档也没有为了显得完整而添加相册授权。

八、自动化与构建证据

shell 复制代码
flutter analyze
flutter test
node --test test/ohos_lifecycle_test.cjs
cd example
flutter test
flutter build hap --debug --no-codesign

10 项 Dart 单测、1 项 widget 测试、8 项执行实际 ArkTS 源码的生命周期测试通过,共 19 项;静态分析无问题。无签名 HAP 构建成功,但 Flutter embedding 和模板有工具链警告,因此不能写成"零警告构建"。

远程依赖真机使用的代码 SHA 为 1da71f4294faa3fc2584c720e3d75df770893236,后面的两个提交只补设备验证说明。

图 5:Flutter 复跑与 19 项 Dart/ArkTS 用例统计。

图 6:HAP 摘要及真机宿主锁定的代码 SHA。

九、真机截图、取消和重订阅

yaml 复制代码
dependencies:
  flutter_screenshot_detect:
    git:
      url: https://atomgit.com/oh-flutter/flutter_screenshot_detect.git
      ref: 1da71f4294faa3fc2584c720e3d75df770893236

执行 flutter pub get 后,应在 pubspec.lock 中确认 resolved-ref 等于上述真机受测 SHA。分支可用于查看最新文档,但不能替代可复现依赖。

在 API 26 真机中,我通过 hdc shell uitest uiInput keyEvent 17 18 注入音量减和电源组合键,让系统真实执行截图。订阅期间收到一次事件,method=ohos_window_screenshotpath=null、时间戳有效,计数为 1。取消订阅后再次截图,约 50 秒内计数仍是 1;重新订阅再截图,计数变为 2。

2026 年 9 月 11 日补图时,我又用系统 snapshot_display 做了一轮可视化复验。图 1 抓取的正是计数为 0 的初始页,这次系统抓图随后触发第一个事件。我暂停监听后再抓图,计数仍为 1;恢复监听时也仍为 1;最后再执行一次系统截图,页面才增加到 2。这组图把"启动、收到、暂停、恢复、再次收到"五个状态保留了下来。

图 7:暂停后页面显示 Stopped,已收到的第一条事件仍然保留。

图 8:暂停期间再次系统截图后重新订阅,计数仍为 1,证明停止状态没有收到新事件。

图 9:恢复订阅后再截图,计数变为 2,列表同时显示两条真实事件。

测试期间系统时区被另一项验收改动,hilog 本地时间回拨一小时,因此日志不能只按显示时钟排序。进程 ID、事件计数和操作顺序一起看,才能避免把第二次事件误判成更早发生。

本次只验证了该机型的组合键截图。控制中心截图、后台投递、Ability 重建和多实例真机行为没有覆盖;多实例与生命周期仅有自动化证据。

图 10:首次截图、取消无事件与重订阅后第二次事件的真机验证记录。

十、提交和远端分支

shell 复制代码
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 flutter_screenshot_detect"
git push -u origin feat/ohos_flutter_screenshot_detect_0.1.7

远端仓库公开,默认分支为适配分支。2026 年 9 月 12 日匿名核对 HEAD 为 609b0793a6f0a6917cc4a4b6f7ec717fbd141bea。当前没有适配 TAG,业务依赖应继续锁定页首受测提交。

十一、FAQ

Q1:为什么 path 是 null

  • 现象: 事件有时间但没有截图路径。
  • 原因: 鸿蒙公共窗口截图回调不提供保存位置。
  • 解决方法: 业务把事件当通知使用,不尝试读取相册或拼接虚假路径。
  • 验证结果: 真机事件稳定返回 path=null

Q2:dispose 一个实例后另一个实例也没事件

  • 现象: 多个 detector 互相影响。
  • 原因: 每个实例重复占用同一 EventChannel 底层 handler。
  • 解决方法: Dart 层共享广播流,每个实例只取消自己的订阅。
  • 验证结果: 多实例自动化回归通过,真机场景待补。

Q3:取消后为什么还要做 generation 判断

  • 现象: 异步窗口查询或排队 callback 可能晚到。
  • 原因: off() 不能倒转已经完成的异步步骤。
  • 解决方法: 取消和解绑时递增代次,注册前和回调时都核对。
  • 验证结果: 8 项原生生命周期测试覆盖旧回调隔离。

十二、总结

flutter_screenshot_detect 已在 OHOS 上接入当前窗口截图事件,并保持原事件流用法。19 项自动化、HAP 构建和真机"收到、取消、恢复"三段验证分别覆盖代码、工程和系统行为。它只能通知截图,不能阻止截图、读取文件或检测录屏;这三个边界应在产品设计中明确保留。

十三、参考链接

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

相关推荐
GLAB-Mary3 小时前
90%的网络工程师,根本没必要考HCIE!
网络·华为·华为认证·hcie·hcia·hcip
HwJack209 小时前
HarmonyOS批量数据与性能优化实战:万条数据入库与不卡 UI 的查询
ui·性能优化·harmonyos
FrameNotWork11 小时前
HarmonyOS应用《左右相册》 隐私防窥实战:用 dlpAntiPeep 让相册“感知“正在被偷瞄
华为·harmonyos
昇腾知识体系11 小时前
昇腾 950 RegBase 性能优化:从 msprof 采数到优化手法
人工智能·华为·性能优化·知识图谱
马剑威(威哥爱编程)14 小时前
HarmonyOS 应用上架审核避坑:驳回五类地图与提交前自检清单
华为·harmonyos
Veer Han17 小时前
AI 开发真实案例完整复盘:给 App 增加宠物功能
人工智能·鸿蒙·宠物
OH_TPC18 小时前
【鸿蒙优选三方库】@react-native-ohos/react-native-screens:原生屏幕导航管理
华为·harmonyos·鸿蒙
西西学代码18 小时前
尾灯-应用层通信协议(BLE)
flutter
昇腾知识体系21 小时前
Ascend950 Matmul 新范式:UB 输入(VECOUT/TSCM)、CV 直通通路与输出 LocalTensor
人工智能·华为·知识图谱