
适配仓库https://atomgit.com/oh-flutter/screen_security适配分支ohos-adaptation这次我适配的是screen_security。它原本已经支持 Android 和 iOS作用也很好理解进入登录、支付、证件展示这类敏感页面后应用可以暂时禁止常规截图和录屏离开页面时再恢复。Android 端用的是FLAG_SECUREiOS 端使用安全文本渲染层但项目里没有鸿蒙实现。我没有另起炉灶改 Dart API而是保留原来的enable()和disable()只在插件底层增加 OHOS 平台代码。最终结果是关闭防护时页面可以正常被抓取开启后系统抓到的应用区域会变成黑色窗口服务里的isPrivacyMode也会从false变成true。这两个结果放在一起才能说明不是按钮只改了页面文案而是系统窗口的隐私模式真的生效了。本文的真机证据覆盖系统截图和窗口隐私状态。OHOS 实现使用系统窗口隐私模式承接屏幕捕获防护但本次没有单独留存系统录屏文件因此不把“录屏已通过真机验证”写进结论。一、最终运行效果先看真机结果。防护关闭时系统截图可以完整抓取 Flutter 页面图 1华为畅享 90 Pro Max、HarmonyOS 7.0.0.105 上防护关闭时可以正常抓取示例页面。点击Enable Security后系统抓图中的应用窗口变为黑色状态栏仍然可见因为它属于系统窗口不属于当前 Flutter 应用窗口图 2隐私模式开启后系统抓图自动遮黑应用窗口内容。为了排除黑屏或渲染失败我又对比了同一应用窗口的系统属性。调用前后isPrivacyMode由false变为true调用disable()后再恢复为false。图 3同一窗口在调用前后发生false - true的状态变化关闭防护后恢复为false。验证点实测结果证据签名 HAP 安装与启动通过Demo 在真机前台正常运行图 1enable()isPrivacyMode: false - true系统抓图中应用区域变黑图 2、图 3disable()isPrivacyMode: true - false页面恢复正常抓取图 1、图 3Dart 通道与异常透传26 项测试通过图 7系统录屏本次未单独留存录屏文件不列为实测通过项验证边界二、成果速览项目内容库名称与版本screen_security 1.1.2上游基线TAGv1.1.2提交1700cf3880ecc4e3c63008caa91da79c48ede78a开源许可证MIT适配仓库oh-flutter/screen_security适配分支ohos-adaptation属于统一命名规范实施前创建的历史分支适配 TAG尚未发布适配提交477879990d220cb517986f85a0f8e9e7fd985399feat: add OpenHarmony screen security support新增 OHOS 能力插件注册、Ability 生命周期、窗口隐私模式和权限声明保持不变的接口ScreenSecurity.enable()、disable()和kidpech_screen_security通道Flutter OH 实测版本3.41.10-ohos-1.0.1核对时的最新稳定标签不是版本号最大的预览标签自动化验证flutter analyze无问题26 项 Dart 测试通过暂无覆盖插件核心行为的 ArkTS 自动化测试构建验证unsigned HAP 构建成功真机结论截图遮黑与isPrivacyMode: false - true - false通过录屏未单独留证图 4Dart API、MethodChannel、ArkTS 插件和系统窗口隐私模式之间的调用关系。三、先确认这个库值得适配动手之前我先在 Flutter 鸿蒙三方库适配清单中做了去重。2026 年 9 月 7 日检查时screen_security只出现在待适配清单里没有出现在“适配中”和“已适配”清单oh-flutter组织下也没有同名仓库。这一步很重要因为功能实现完才发现别人已经提交前面的时间基本就白花了。原项目的 Dart 层已经把接口封装好了调用方只需要写importpackage:screen_security/screen_security.dart;finalscreenSecurityScreenSecurity();FuturevoidprotectSensitiveContent()async{awaitscreenSecurity.enable();}FuturevoidrestoreNormalCapture()async{awaitscreenSecurity.disable();}再往下看默认实现通过名为kidpech_screen_security的MethodChannel调用原生端方法名分别是enableScreenSecurity和disableScreenSecurity。所以鸿蒙端真正要补的是三件事注册同名通道、接住这两个方法、把开关状态交给鸿蒙窗口 API。四、本次实测环境项目本次使用情况电脑Apple Silicon Macarm64Flutter OH3.41.10-ohos-1.0.1Dart3.11.5DevEco Studio26.0.0 ReleaseHarmonyOS SDKAPI 26测试手机华为畅享 90 Pro MaxHarmonyOS 7.0.0.105插件screen_security 1.1.2这里需要区分“版本号最新”和“稳定版最新”。活动表推荐 Flutterv3.44.9截至 2026 年 9 月 11 日Flutter OH 仓库中版本号最大的标签是3.44.9ohos-0.0.1-canary1但它带有canary1属于预览版本。当前最新正式稳定标签仍是3.41.10-ohos-1.0.1也是本文实际完成静态检查、测试、HAP 构建和真机验证的版本。因此这里不能只把表格改成3.44.9否则版本与证据不一致。如果活动最终强制要求v3.44.9应先用上述 canary SDK 重新完成构建和真机回归再替换环境信息与测试证据后续出现3.44.9正式稳定标签时也要重新验证。五、代码仓库是怎么来的上游基线是最新稳定版screen_security 1.1.2TAGv1.1.2指向提交1700cf3880ecc4e3c63008caa91da79c48ede78a。该版本保持enable()和disable()两个公开接口稳定许可证为 MIT允许保留版权与许可声明后进行修改和再发布因此我选择它作为适配起点而不是更早版本或未发布代码。我先核对待适配、适配中、已适配清单和oh-flutter组织仓库再把保留上游历史的代码同步到 AtomGit。最终适配提交47787999的直接父提交就是上述上游基线当前可复现入口为gitclone https://atomgit.com/oh-flutter/screen_security.gitcdscreen_securitygitcheckout 477879990d220cb517986f85a0f8e9e7fd985399gitrev-parse HEADgitshow-s--format%P这个仓库早于统一分支规范远端实际分支是ohos-adaptation所以本文不把它改写成并不存在的feat/ohos_screen_security_1.1.2。后续库统一使用feat/ohos_库名_版本。在干净副本中补全 OHOS 插件骨架可使用flutter create--templateplugin--platformsohos --no-pub.命令只负责生成ohos/工程骨架通道名、窗口能力、权限和错误处理仍要按原项目接口实现。适配仓库、分支链接和可复现命令已经同时保留后续代码图、构建图与真机图分别验证实现和结果。六、让 Flutter 识别 OHOS 插件第一处改动在pubspec.yaml。原来只注册了 Android 和 iOS我增加了 OHOS 平台入口flutter:plugin:platforms:android:package:dev.kidpech.screen_securitypluginClass:KidpechScreenSecurityPluginios:pluginClass:KidpechScreenSecurityPluginohos:pluginClass:ScreenSecurityPlugin这里的pluginClass必须和 ArkTS 导出的类名一致。少写这一段时Dart 代码照样能通过静态检查但运行到鸿蒙设备后找不到插件实现调用通道就会失败。这类问题看起来像业务方法没写对实际是插件根本没有注册进引擎。OHOS 插件还需要自己的工程入口。我新建了ohos/index.ets只负责导出插件类importScreenSecurityPluginfrom./src/main/ets/components/plugin/ScreenSecurityPlugin;exportdefaultScreenSecurityPlugin;同时补齐oh-package.json5、hvigorfile.ts、build-profile.json5和src/main/module.json5。这些文件看起来零碎但职责很清楚它们告诉 OHOS 构建系统这是一个 HAR 模块、入口在哪里、由哪个构建插件处理以及需要什么系统权限。七、ArkTS 端怎么接住两个方法核心代码在ScreenSecurityPlugin.ets。这个类同时实现FlutterPlugin、MethodCallHandler和AbilityAware。exportdefaultclassScreenSecurityPluginimplementsFlutterPlugin,MethodCallHandler,AbilityAware{privatechannel:MethodChannel|nullnull;privateabilityContext:common.UIAbilityContext|nullnull;onAttachedToEngine(binding:FlutterPluginBinding):void{this.channelnewMethodChannel(binding.getBinaryMessenger(),kidpech_screen_security,);this.channel.setMethodCallHandler(this);}onAttachedToAbility(binding:AbilityPluginBinding):void{this.abilityContextbinding.getAbility().contextascommon.UIAbilityContext;}}onAttachedToEngine负责建立通道通道名称必须和 Dart 端一字不差。onAttachedToAbility则保存当前UIAbilityContext。之所以需要这个上下文是因为后面要通过它找到应用当前正在显示的窗口。生命周期也不能只管“连上”还要管“断开”。插件离开 Flutter 引擎时要取消方法处理器离开 Ability 时要清空上下文。否则插件被重新挂载后旧对象还可能留在内存里问题不一定马上出现但调试起来很麻烦。onDetachedFromEngine(binding:FlutterPluginBinding):void{this.channel?.setMethodCallHandler(null);this.channelnull;}onDetachedFromAbility():void{this.abilityContextnull;}两个 Dart 方法来到 ArkTS 后用一个switch分发即可onMethodCall(call:MethodCall,result:MethodResult):void{switch(call.method){caseenableScreenSecurity:this.setWindowPrivacyMode(true,result);break;casedisableScreenSecurity:this.setWindowPrivacyMode(false,result);break;default:result.notImplemented();break;}}这里我没有复制两套开关逻辑而是统一交给setWindowPrivacyMode。开启和关闭只差一个布尔值放在一起更不容易出现一边修了、另一边忘记改的情况。八、真正打开鸿蒙窗口隐私模式鸿蒙端的关键 API 是setWindowPrivacyMode。调用前先使用window.getLastWindow(context)拿到当前窗口privateasyncsetWindowPrivacyMode(enabled:boolean,result:MethodResult,):Promisevoid{constcontextthis.abilityContext;if(contextnull){result.error(NO_ABILITY,UIAbility is not available,null);return;}try{constcurrentWindowawaitwindow.getLastWindow(context);awaitcurrentWindow.setWindowPrivacyMode(enabled);result.success(null);}catch(exception){consterrorexceptionasBusinessError;result.error(error.code.toString(),Failed to update window privacy mode,error.message,);}}这段代码里有两个容易忽略的地方。第一Ability 还没准备好时不能硬调窗口 API所以我先判断上下文是否为空并把NO_ABILITY返回给 Dart。第二系统 API 是异步调用失败后也不能只在 ArkTS 控制台打印一行日志否则 Flutter 页面不知道发生了什么。通过result.error把错误码和信息传回去调用方才有机会弹提示或做降级处理。图 5核心实现只有一条主线获取当前窗口再按参数打开或关闭隐私模式。九、权限别漏掉只写 API 还不够模块需要声明ohos.permission.PRIVACY_WINDOW{ module: { name: screen_security, type: har, deviceTypes: [default, tablet], requestPermissions: [ { name: ohos.permission.PRIVACY_WINDOW } ] } }插件最终会以 HAR 的形式进入示例应用。构建完成后我直接检查 HAP 内的module.json可以看到ohos.permission.INTERNET和ohos.permission.PRIVACY_WINDOW都已经合并进去。这样比只看源码更可靠因为源码里写了权限不等于最终安装包里一定有。图 6从构建产物中检查权限确认PRIVACY_WINDOW已进入最终模块配置。十、补齐交付文件并完成自动化检查除 OHOS 源码外我还核对了README.md、CHANGELOG.md、许可证、pubspec.yaml平台声明、example/ohos/和示例截图。交付文件必须反映真实仓库状态目标仓库没有要求的文件不为凑清单虚构后续若按社区准入规则提交再以当时规则补齐README.OpenSource等材料。代码写完后我先在插件根目录执行flutter analyze fluttertestflutter analyze返回No issues found插件根目录的 Dart 测试共 26 项全部通过。测试覆盖了通道名称、开启与关闭的方法调用、无参数调用、原生异常向 Dart 端传递以及多次开关的调用顺序。仓库虽然带有example/ohos/entry/src/ohosTest/模板测试骨架但它没有断言插件的窗口隐私行为因此本文不把它计入 ArkTS 功能测试。图 7静态检查无报错26 项 Dart 测试全部通过。接着进入example构建 OHOS 调试包cdexample flutter build hap--debug--no-codesignHvigor 构建成功生成了build/ohos/hap/entry-default-unsigned.hap。--no-codesign适合检查代码和工程配置能不能正常编译真机安装仍然要使用 DevEco Studio 配置过签名的 HAP。不要把“unsigned HAP 构建成功”直接写成“真机验证完成”这是两回事。图 8OHOS 示例工程完成构建Hvigor 正常输出 unsigned HAP。十一、Demo 通过固定提交接入另一个应用要复用这次适配依赖必须指向 AtomGit 上经过验证的完整提交不能落回 pub.dev 上尚未包含 OHOS 实现的版本也不要长期依赖会继续移动的分支dependencies:screen_security:git:url:https://atomgit.com/oh-flutter/screen_security.gitref:477879990d220cb517986f85a0f8e9e7fd985399执行flutter pub get后应在pubspec.lock的resolved-ref中确认解析结果仍是477879990d220cb517986f85a0f8e9e7fd985399。分支链接适合查看最新代码TAG 或完整 commit 才适合作为可复现依赖当前没有适配 TAG所以这里锁定 commit。下面是包含导入、调用、状态展示和错误反馈的最小页面importpackage:flutter/material.dart;importpackage:screen_security/screen_security.dart;voidmain(){runApp(constMaterialApp(home:Scaffold(body:SafeArea(child:ScreenSecurityDemo()),),),);}classScreenSecurityDemoextendsStatefulWidget{constScreenSecurityDemo({super.key});overrideStateScreenSecurityDemocreateState()_ScreenSecurityDemoState();}class_ScreenSecurityDemoStateextendsStateScreenSecurityDemo{final_screenSecurityScreenSecurity();bool _enabledfalse;String?_error;Futurevoid_setEnabled(bool enabled)async{try{if(enabled){await_screenSecurity.enable();}else{await_screenSecurity.disable();}if(!mounted)return;setState((){_enabledenabled;_errornull;});}catch(error){if(!mounted)return;setState(()_errorerror.toString());}}overrideWidgetbuild(BuildContextcontext){returnPadding(padding:constEdgeInsets.all(24),child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[Text(_enabled?Screen security is ON:Screen security is OFF),if(_error!null)Text(Error:$_error),FilledButton(onPressed:_enabled?null:()_setEnabled(true),child:constText(Enable Security),),OutlinedButton(onPressed:_enabled?()_setEnabled(false):null,child:constText(Disable Security),),],),);}}这个插件在 Dart 层不创建订阅或控制器因此没有额外对象需要dispose()。但是隐私模式属于窗口状态退出敏感流程前仍要显式await screenSecurity.disable()不要只在无法等待异步结果的dispose()中恢复。十二、真机上怎么判断防护真的生效我把签名后的调试 HAP 安装到华为畅享 90 Pro Max。应用启动后红色卡片显示Screen security is OFF按钮文字是Enable Security。这时系统窗口信息中的isPrivacyMode为false系统抓图可以看到完整内容对应图 1。点击Enable Security后Flutter 页面内部会切换到开启状态同时原生插件把窗口隐私模式设置为true。我用窗口服务再次查询得到WindowName: flutter_oh_demo0 isPrivacyMode: true这时候再次通过系统抓图应用内容区域已经变成黑色对应图 2。为了避免只拿一张黑图下结论我把关闭和开启两次窗口查询放在一起对比图 3 证明状态变化确实落到了系统窗口层。测试结束后我又调用了一次disable()确认isPrivacyMode回到false。这个收尾不能省因为真实业务通常只需要在敏感页面临时开启。如果退出页面后没有恢复用户在应用其他页面也无法截图体验会很差。实际接入时可以在进入敏感流程时调用enable()离开时在合适的生命周期里调用disable()同时注意异常和页面跳转。这轮没有单独留存系统录屏文件。实现采用的窗口隐私模式面向屏幕捕获保护但当前真机结论只覆盖截图路径录屏效果需要在目标系统版本上另行开始录制、切换开关并回看成片后才能标记为通过。十三、提交适配分支推送前先排除签名材料、本机 SDK 路径和构建产物再提交实际文件gitstatus--shortgitdiff--checkgitadd.metadata CHANGELOG.md README.md pubspec.yaml lib ohos examplegitcommit-s-mfeat: add OpenHarmony screen security supportgitpush-uorigin ohos-adaptation截至 2026 年 9 月 10 日远端分支可读取HEAD 为477879990d220cb517986f85a0f8e9e7fd985399。十四、FAQ这次适配里最容易踩的几个坑Q1调用后为什么没有进入 OHOS 实现现象enable()或disable()没有到达 ArkTS 方法分支调用可能表现为插件未注册或方法未实现。原因pubspec.yaml平台入口、pluginClass、通道名或方法名没有与原接口保持一致。解决方法注册ScreenSecurityPlugin并逐字核对kidpech_screen_security、enableScreenSecurity和disableScreenSecurity。验证结果26 项 Dart 测试覆盖通道名称、两个方法名和调用顺序真机开关可以改变窗口隐私状态。Q2为什么要返回NO_ABILITY现象插件已经连接 Flutter 引擎但当前还拿不到可用于查询窗口的UIAbilityContext。原因引擎注册和 Ability 挂载是两个生命周期阶段不能假设它们同时完成。解决方法在onAttachedToAbility保存上下文在调用窗口 API 前判空并在解绑时清空。验证结果最终源码会在上下文缺失时返回明确的NO_ABILITYDart 测试确认平台异常不会被吞掉。Q3源码声明了权限为什么还要检查 HAP现象源码中已经写入ohos.permission.PRIVACY_WINDOW但仅凭源码无法证明宿主最终获得了该声明。原因插件会先构建为 HAR再由宿主合并配置中间的工程配置或依赖解析可能影响最终产物。解决方法构建 HAP 后解包检查最终module.json不要只检查插件目录。验证结果图 6 显示PRIVACY_WINDOW已进入本次受测 HAP。Q4开启后截图为什么只剩黑色现象系统截图只保留状态栏应用内容区域变成黑色。原因窗口隐私模式阻止系统抓图暴露受保护的应用窗口这不是 Flutter 渲染失败。解决方法同时保留开启前页面、开启后抓图和窗口属性避免只凭黑图下结论。验证结果图 1、图 2 和图 3 分别证明页面原本正常、抓图被遮黑以及isPrivacyMode已开启。Q5能否把它当成完整的数据防泄漏方案现象业务容易把“窗口进入隐私模式”理解成所有复制途径都被阻断。原因窗口级保护挡不住另一部手机拍摄、已被修改的设备或数据在进入受保护界面之前泄漏。解决方法把插件作为纵深防护的一层同时保留身份认证、最小权限、敏感字段脱敏和服务端鉴权。验证结果本次只确认系统截图遮黑和窗口状态切换录屏未单独留证外部拍摄也不在插件能力范围内。十五、总结这次适配没有改动screen_security的上层用法原有 Flutter 代码继续调用enable()和disable()。新增工作集中在 OHOS 插件注册、Ability 生命周期、MethodChannel 方法分发、窗口隐私模式调用和权限声明几个地方。最后我用四层结果做了确认Dart 静态检查通过、26 项自动化测试通过、OHOS HAP 构建通过、HarmonyOS API 26 真机上的窗口隐私模式可以在false和true之间切换。开启后系统抓图中的应用区域变黑关闭后恢复正常。当前仍缺少插件核心行为的 ArkTS 自动化测试和独立录屏证据因此这两项没有写成已经通过。十六、参考链接适配仓库适配分支Flutter OH 版本标签Flutter OH 环境搭建指南欢迎加入CPF-Flutter 鸿蒙社区https://atomgit.com/CPF-Flutter