Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 system_alert_window

Android 的悬浮窗插件经常依赖跨应用显示窗口能力。迁移到鸿蒙时,不能只把插件目录改成 ohos,还要先确认权限模型、窗口类型和后台限制有没有直接等价 API。

本文以 system_alert_window 2.0.11 为例,记录 Flutter 3.47 鸿蒙适配过程:先找出原插件真实使用的 Channel,修正 Dart 与 ArkTS 的通道不一致问题,再让 OHOS 端准确返回悬浮窗权限不可用。文章不伪造“悬浮窗已经显示”,而是用真机验证正确的限制结果。

一、最终效果

在这里插入图片描述{:width=“50%”}

图 1:API 26 真机启动适配后的 Demo。

在这里插入图片描述{:width=“50%”}

图 2:真机检查后返回 false,页面显示当前平台不支持。

验证点真机结果
Flutter3.47.4-ohos-1.0.4
HarmonyOS7.0.0.105,API 26
HAPsigned HAP 构建通过
ChannelDart 与 ArkTS 统一为 in.jvapps.system_alert_window
权限检查checkPermissions 返回 false
适配结论不伪造 Android 跨应用悬浮窗能力

二、先理解原插件的真实能力

原插件提供的主要接口包括:

SystemAlertWindow.checkPermissions();
SystemAlertWindow.requestPermissions();
SystemAlertWindow.showSystemWindow();
SystemAlertWindow.updateSystemWindow();
SystemAlertWindow.closeSystemWindow();
SystemAlertWindow.isBubbleMode();

这些方法在 Android 上围绕悬浮窗权限、WindowManager 和后台服务工作。它们不是普通 Flutter Overlay:

能力普通 Flutter OverlayAndroid 悬浮窗鸿蒙端本次结论
覆盖当前 Flutter 页面可以可以可以继续做页面内 UI
覆盖其他应用不可以可以申请权限没有直接等价实现
后台悬浮服务不可以依赖 Service需要单独 Ability 和权限方案
Bubble 模式不适用Android 专属返回 false

本次适配的边界是:保留原 Dart API、完成 OHOS 插件注册和权限查询,同时明确告诉业务层当前系统能力不可用。

三、准备 Flutter 3.47 和设备

本文实际环境:

工具版本
Flutter OH3.47.4-ohos-1.0.4
Dart3.13.3
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 26
测试设备127.0.0.1:5555,HarmonyOS 7.0.0.105

环境没有准备好时,先看 Flutter OH 环境搭建指南。

export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter --version
hdc list targets

四、拉取适配分支

本文使用的仓库信息:

git clone https://atomgit.com/oh-flutter/system_alert_window.git
cd system_alert_window
git switch feat/ohos_system_alert_window_2.0.11
git log -1 --oneline

五、适配过程中的第一个问题:Channel 不一致

原 Dart 文件从常量中读取通道:

static const String CHANNEL = 'in.jvapps.system_alert_window';
static const MethodChannel _channel =
    MethodChannel(Constants.CHANNEL);

原 OHOS 插件骨架监听的却是:

new MethodChannel(
  binding.getBinaryMessenger(),
  'system_alert_window',
);

两个字符串不同,Dart 调用就不会到达 ArkTS。适配时统一为:

this.channel = new MethodChannel(
  binding.getBinaryMessenger(),
  'in.jvapps.system_alert_window',
);

Channel 名称不是文件名,也不是 pub 包名,而是 Dart 和原生两边共同约定的通信地址。

六、第二个问题:Codec 也要对齐

原 Dart 使用 JSONMethodCodec,OHOS embedding 默认使用 StandardMessageCodec。两边 Codec 不一致时,参数可能无法解析,运行表现可能像插件没有响应。

本次把 Dart 端统一为默认 MethodChannel:

static const MethodChannel _channel =
    MethodChannel(Constants.CHANNEL);

适配时需要同时核对:

项目适配值
Channelin.jvapps.system_alert_window
CodecFlutter 默认 StandardMessageCodec
权限方法checkPermissions、requestPermissions
生命周期onAttachedToEngine、onDetachedFromEngine

七、声明 OHOS 插件平台

pubspec.yaml 增加:

flutter:
  plugin:
    platforms:
      android:
        package: in.jvapps.system_alert_window
        pluginClass: SystemAlertWindowPlugin
      ios:
        package: in.jvapps.system_alert_window
        pluginClass: SystemAlertWindowPlugin
      ohos:
        pluginClass: SystemAlertWindowPlugin

OHOS 入口:

import SystemAlertWindowPlugin from './src/main/ets/components/plugin/SystemAlertWindowPlugin';

export default SystemAlertWindowPlugin;

插件注册通道,并在引擎解绑时释放 handler:

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(
    binding.getBinaryMessenger(),
    'in.jvapps.system_alert_window',
  );
  this.channel.setMethodCallHandler(this);
}

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  this.channel?.setMethodCallHandler(null);
  this.channel = null;
}

八、逐个映射权限和窗口方法

Dart 侧权限检查会发送:

_channel.invokeMethod(
  'checkPermissions',
  [Commons.getSystemWindowPrefMode(prefMode)],
);

OHOS 端处理:

case 'checkPermissions':
  result.success(false);
  return;

case 'requestPermissions':
  result.success(false);
  return;

case 'isBubbleMode':
  result.success(false);
  return;

关闭窗口是幂等操作,可以返回空结果:

case 'closeSystemWindow':
  result.success(null);
  return;

没有直接等价实现的跨应用悬浮窗方法不能返回 true。业务层需要知道当前平台没有完成这项能力。

九、创建最小 Demo

Demo 启动时自动查询权限:


void initState() {
  super.initState();
  _check();
}

Future<void> _check() async {
  final allowed = await SystemAlertWindow.checkPermissions(
    prefMode: SystemWindowPrefMode.OVERLAY,
  );
  if (!mounted) return;
  setState(() {
    _status = allowed == true ? '允许' : '当前平台不支持';
    _detail = 'checkPermissions 返回:$allowed';
  });
}

页面还提供重新检查按钮。真机显示:

当前平台不支持
checkPermissions 返回:false

这个 Demo 验证的是插件适配结果,不会弹出 Android 权限申请流程。

十、构建、签名和安装

flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign

配置 DevEco 调试签名后:

flutter build hap --debug
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

本文使用 signed HAP 在 API 26 真机启动和检查权限。签名文件和密码没有提交。

十一、真机验证

真机启动后,页面自动读取 checkPermissions,显示“当前平台不支持”。点击重新检查后仍然返回 false。

完整链路如下:

  1. Dart 调用原插件 API。
  2. MethodChannel 使用上游约定的 Channel。
  3. StandardMessageCodec 传递参数。
  4. ArkTS 插件被加载。
  5. OHOS 端返回准确的平台能力结果。

本次没有把 Android 跨应用悬浮窗伪装成鸿蒙已支持。真正的跨应用窗口还需要单独研究 HarmonyOS UIExtension、WindowStage、权限和后台生命周期。

十二、常见问题

Q1:为什么不直接返回 true

当前没有完成跨应用悬浮窗能力。返回 true 会让业务以为窗口已经可以显示,实际却看不到覆盖窗口。

Q2:为什么 Channel 要用 in.jvapps.system_alert_window

这是原 Dart 代码的真实常量。原生端必须匹配它,不能根据仓库名自行改成 system_alert_window。

Q3:为什么要去掉 JSONMethodCodec

OHOS embedding 端默认使用标准消息编解码。两边 Codec 不一致时,参数可能无法解析,表现可能像插件没有响应。

Q4:页面内 Flutter Overlay 能不能继续使用

页面内 Overlay 属于 Flutter 自己的渲染范围,可以单独实现。本文验证的是跨应用悬浮窗能力,不能把两者混在一起。

Q5:返回 false 算适配完成吗

本文完成的是 OHOS 平台注册、通道对齐和准确能力返回。跨应用悬浮窗原生能力仍需另一个独立实现任务,不能用 false 冒充完整悬浮窗。

十三、复现命令

git clone https://atomgit.com/oh-flutter/system_alert_window.git
cd system_alert_window
git switch feat/ohos_system_alert_window_2.0.11
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 真实 Channel、Codec、方法名和权限语义四个方面排查。Android 跨应用悬浮窗在鸿蒙端没有直接等价实现时,最可靠的做法是保留 API、统一通道、返回准确的 false,并让业务层可以明确降级。

十五、参考资料与仓库地址

本文适配仓库

上游和包信息

Flutter 和鸿蒙资料

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐