Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 把 flutter_windowmanager_plus 跑到真机
Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 把 flutter_windowmanager_plus 跑到真机
很多 Flutter 插件在 Android 上能直接用,换到鸿蒙后却会遇到两个问题:插件的 pubspec.yaml 没有声明 ohos,原生目录里也没有 ArkTS 实现。新手第一次处理这类问题时,最容易卡在“代码应该放在哪里”和“怎么证明真的跑在鸿蒙设备上”。
本文用 flutter_windowmanager_plus 1.1.1 做完整示例。这个库原本提供 Android WindowManager.LayoutParams 风格的接口,我把其中两个能映射到鸿蒙窗口的能力接上:保持当前 Flutter 窗口常亮,以及打开或关闭防截屏。文章中的命令、版本、提交和图片都来自一次真实构建、安装和设备操作。
适配仓库和分支如下:
- 仓库:https://atomgit.com/oh-flutter/flutter_windowmanager_plus
- 分支:
feat/ohos_flutter_windowmanager_plus_1.1.1 - 本文示例提交:
b3b5822698e08a751dc2e0ea273dae5ec2130c56
一、先看最终结果
图 1:HarmonyOS API 26 真机启动适配后的 Demo,页面提供常亮和防截屏按钮。
图 2:点击“开启屏幕常亮”后,Dart 层收到 true。这个结果证明 Flutter 调到了 OHOS 插件,而不是只显示静态页面。
图 3:开启防截屏后,系统截图变成黑屏。这是 setWindowPrivacyMode(true) 的预期保护效果;按钮状态通过 UI 树回读为 防截屏 开启:true。
图 4:两个能力同时开启时,系统截图仍然是黑屏,说明受保护窗口没有把页面内容交给截图工具。
图 5:关闭常亮后,页面回读 常亮 关闭:true,两个开关分别管理。
图 6:重启 Demo 后回到默认状态,窗口重新可见,便于继续下一轮验证。
| 验证点 | 真机结果 |
|---|---|
| Flutter 版本 | 3.47.4-ohos-1.0.4 |
| 鸿蒙系统 | HarmonyOS 7.0.0.105,API 26 |
| HAP 构建 | 通过,生成 signed HAP |
| 屏幕常亮 | addFlags(FLAG_KEEP_SCREEN_ON) 返回 true |
| 防截屏 | setSecure(true) 返回 true |
| 状态恢复 | 常亮、防截屏均可独立关闭 |
二、准备工具和设备
如果还没有 Flutter 鸿蒙环境,先按照 Flutter OH 环境搭建指南 完成安装。本文不把环境安装写成一长串猜测,直接从已经能识别真机的环境开始。
本文实际使用的版本如下:
| 工具 | 版本 |
|---|---|
| Flutter OH | 3.47.4-ohos-1.0.4 |
| Dart | 3.13.3 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26 |
| 测试设备 | 127.0.0.1:10003,HarmonyOS 7.0.0.105 |
先检查 Flutter 版本和设备是否在线:
export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter --version
hdc list targets
新手只需要确认两件事:第一行版本中包含 3.47.4-ohos;hdc list targets 至少能看到一个 ohos-arm64 设备。设备序列号不要截图发布,文章中只保留脱敏后的连接地址。
三、确认上游库和适配范围
上游包名是 flutter_windowmanager_plus,本文锁定 pub.dev 最新版本 1.1.1。它的 Dart 层接口主要是:
FlutterWindowManagerPlus.addFlags(int flags)
FlutterWindowManagerPlus.clearFlags(int flags)
FlutterWindowManagerPlus.setSecure(bool secure)
原库的语义来自 Android。鸿蒙没有 WindowManager.LayoutParams 这个类,所以不能把 Android 常量逐个照搬。本文只做有明确鸿蒙对应关系的两项:
| 原接口 | 鸿蒙实现 | 说明 |
|---|---|---|
FLAG_KEEP_SCREEN_ON | Window.setWindowKeepScreenOn() | 控制当前前台窗口常亮 |
FLAG_SECURE / setSecure | Window.setWindowPrivacyMode() | 防止窗口内容出现在截屏中 |
| 其他 Android flags | 不实现 | 没有直接等价的鸿蒙窗口 API |
四、从 AtomGit 拉代码
打开终端,执行下面的命令:
git clone https://atomgit.com/oh-flutter/flutter_windowmanager_plus.git
cd flutter_windowmanager_plus
git switch feat/ohos_flutter_windowmanager_plus_1.1.1
git branch --show-current
如果看到的分支名不是 feat/ohos_flutter_windowmanager_plus_1.1.1,先不要继续改代码。适配分支是后续锁定依赖和复现问题的依据。
五、补全 OHOS 插件骨架
Flutter 识别平台插件,靠的是 pubspec.yaml 中的 flutter.plugin.platforms。在 Android 和 iOS 后面补充:
flutter:
plugin:
platforms:
android:
package: com.ijashuzain.flutter_windowmanager_plus
pluginClass: FlutterWindowmanagerPlusPlugin
ios:
pluginClass: FlutterWindowmanagerPlusPlugin
ohos:
pluginClass: FlutterWindowmanagerPlusPlugin
这段配置的意思是:当宿主是鸿蒙时,Flutter 去加载 FlutterWindowmanagerPlusPlugin。插件的 OHOS 入口文件是:
ohos/index.ets
ohos/src/main/ets/components/plugin/FlutterWindowmanagerPlusPlugin.ets
入口文件只做导出:
import FlutterWindowmanagerPlusPlugin from './src/main/ets/components/plugin/FlutterWindowmanagerPlusPlugin';
export default FlutterWindowmanagerPlusPlugin;
新手不要把业务页面写进这个文件。页面仍然在 Flutter 的 example/lib/main.dart,ArkTS 文件只负责把 Dart 方法翻译成鸿蒙 API。
六、理解 Dart 到 ArkTS 的调用链
整个调用过程可以按四层理解:
按钮点击
-> FlutterWindowManagerPlus.addFlags / setSecure
-> MethodChannel('flutter_windowmanager_plus')
-> FlutterWindowmanagerPlusPlugin.onMethodCall
-> OHOS Window API
Dart 层只需要把参数发出去。为了让鸿蒙也走到通道,_isSupported 增加了 Platform.operatingSystem == 'ohos' 判断:
static bool get _isSupported =>
!kIsWeb &&
(defaultTargetPlatform == TargetPlatform.android ||
Platform.operatingSystem == 'ohos');
如果忘记这一处,鸿蒙端会在 Dart 层直接返回 false,ArkTS 代码根本不会执行。这是这类适配最容易漏掉的地方。
七、实现鸿蒙窗口能力
7.1 获取当前主窗口
插件不能凭空创建一个窗口,需要从当前 UIAbility 取得 WindowStage,再取得主窗口。为此,插件实现 AbilityAware:
onAttachedToAbility(binding: AbilityPluginBinding): void {
this.ability = binding.getAbility();
}
private getMainWindow(): window.Window {
const stage = FlutterManager.getInstance().getWindowStage(this.ability);
return stage.getMainWindowSync();
}
页面退出或 Flutter 引擎解绑时,插件清空 Ability 引用。这样可以避免把旧页面的窗口对象留在内存里。
7.2 映射常亮和防截屏
Android 的 FLAG_KEEP_SCREEN_ON 数值是 0x00000080,FLAG_SECURE 数值是 0x00002000。鸿蒙端只识别这两个值:
if ((flags & 0x00000080) !== 0) {
await mainWindow.setWindowKeepScreenOn(enabled);
}
if ((flags & 0x00002000) !== 0) {
await mainWindow.setWindowPrivacyMode(enabled);
}
setSecure(true) 直接调用 setWindowPrivacyMode(true)。这个 API 需要 ohos.permission.PRIVACY_WINDOW,所以 Demo 的 example/ohos/entry/src/main/module.json5 增加了权限声明:
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.PRIVACY_WINDOW" }
]
这里的权限只服务防截屏。常亮调用不需要额外权限。
7.3 明确不支持的 flags
Android 还有全屏、触摸、壁纸、锁屏等 flags。它们和鸿蒙的窗口模型并不一一对应,所以本次没有返回“成功”。未知或不支持的 flags 会返回 false,这样业务可以知道当前平台没有完成等价处理。
八、创建一个新手能看懂的 Demo
在 example/lib/main.dart 中保留四个按钮:
Future<void> _setKeepScreenOn(bool enabled) async {
final ok = enabled
? await FlutterWindowManagerPlus.addFlags(
FlutterWindowManagerPlus.FLAG_KEEP_SCREEN_ON)
: await FlutterWindowManagerPlus.clearFlags(
FlutterWindowManagerPlus.FLAG_KEEP_SCREEN_ON);
if (!mounted) return;
setState(() => _status = '常亮 ${enabled ? '开启' : '关闭'}:$ok');
}
Future<void> _setSecure(bool enabled) async {
final ok = await FlutterWindowManagerPlus.setSecure(enabled);
if (!mounted) return;
setState(() => _status = '防截屏 ${enabled ? '开启' : '关闭'}:$ok');
}
按钮不需要懂原生代码,只要调用公开 Dart API。mounted 判断是 Flutter 页面异步更新的基本保护,防止页面已经退出后还调用 setState。
九、构建、签名和安装
先拉取依赖并构建未签名 HAP:
flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign
未签名 HAP 只能证明工程编译通过,不能直接安装到普通真机。第一次安装时,在 DevEco Studio 打开 example/ohos,进入 File -> Project Structure -> Signing Configs,勾选自动生成调试签名,然后重新构建:
flutter build hap --debug
我的本机真机验证生成了:
example/build/ohos/hap/entry-default-signed.hap
安装和启动命令如下:
hdc list targets
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_oh_demo
文章中的签名文件、证书密码和本机绝对路径没有提交到仓库。新手不要把 build-profile.json5 中的私钥配置复制进公开仓库。
十、按按钮做真机验证
启动后按下面顺序验证:
- 点击“开启屏幕常亮”,页面应显示
常亮 开启:true。 - 点击“开启防截屏”,页面应显示
防截屏 开启:true。 - 分别点击两个关闭按钮,确认每个能力可以独立关闭。
- 用系统截图组合键测试防截屏,系统截图中不应出现受保护窗口内容;如果设备策略或 ROM 限制导致行为不同,要把实际结果写入文章。
本次页面状态回读和截图均来自 API 26 真机,图 2~图 6 对应上述操作顺序。当前验证证明两个窗口 API 调用成功;它不等于后台常驻、CPU 唤醒锁或所有系统截屏入口都已经覆盖。
十一、常见问题
Q1:点击按钮一直显示 false
先检查 Dart 层 _isSupported 是否包含 Platform.operatingSystem == 'ohos',再检查 pubspec.yaml 是否声明了 ohos 平台。少任何一处,ArkTS 都不会收到调用。
Q2:构建提示 SDK 版本不兼容
Flutter 3.47 的 @ohos/flutter_ohos 依赖要求较高的兼容 SDK。把 example/ohos/build-profile.json5 的 compatibleSdkVersion 与 targetSdkVersion 对齐到当前 API 26,再重新构建。
Q3:防截屏调用报权限错误
确认 entry/src/main/module.json5 声明了 ohos.permission.PRIVACY_WINDOW,并且重新安装了新 HAP。只改 Dart 文件而不重新安装宿主,设备上仍然是旧权限配置。
Q4:为什么不把所有 Android flag 都实现
因为没有对应的鸿蒙 API 时,返回 true 会让业务误以为功能已经生效。适配文章应该把已实现、未实现和平台限制分开写。
Q5:flutter test 报 Invalid kernel binary format
这个错误通常是同一个工程混用了不同 Flutter SDK 生成的缓存。先关闭正在运行的 Flutter 命令,再确认 flutter --version 是 3.47.4-ohos-1.0.4,删除工程的 .dart_tool 和 build 后重新执行 flutter pub get。如果仍然出现错误,优先用 DevEco Studio 的 HAP 构建和真机回归确认插件链路,并把本机 Flutter SDK 的缓存问题单独处理,不要把它误判成 ArkTS 业务代码错误。
十二、复现命令汇总
git clone https://atomgit.com/oh-flutter/flutter_windowmanager_plus.git
cd flutter_windowmanager_plus
git switch feat/ohos_flutter_windowmanager_plus_1.1.1
export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign
十三、总结
这次适配真正改动了三层:Dart 层允许鸿蒙进入 MethodChannel,pubspec.yaml 注册 OHOS 平台,ArkTS 层把两个 Android 风格 flag 映射到鸿蒙窗口 API。新手复现时,先确认版本和设备,再拉适配分支,最后按“依赖、分析、测试、构建、签名、安装、真机操作”的顺序执行,就能定位问题发生在哪一层。
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐

所有评论(0)