HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-悬浮窗场景

HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-悬浮窗场景

1.1 场景概述

在移动应用开发中,不同设备的屏幕形态各异,如横竖屏切换、PC、折叠屏等,同时系统状态栏、导航栏、软键盘等元素也会占据屏幕空间。为了确保应用内容在各种设备和场景下都能正常显示,不被遮挡。本文将详细介绍 RN 悬浮窗组件的实现原理、适配指导以及具体的场景案例。

1.1.1 使用场景

在社交应用、内容平台、媒体播放器等应用中,悬浮窗是很常见的交互方式,希望在不同设备上悬浮窗都能正常显示和交互。下面是不同设备上悬浮窗差异,包括:直板机、PAD、PC、折叠屏(阔折叠、双折叠、三折叠)、智慧屏、座舱。

1.1.2 常见问题

开发过程中悬浮窗界面在不同设备上会存在差异,可能会出现以下问题:

  • 悬浮窗被状态栏、导航栏遮挡
  • 悬浮窗位置不当导致界面布局错乱
  • 折叠屏设备折痕区域影响悬浮窗显示
  • 分屏场景下悬浮窗被另一个应用窗口遮挡
  • 横竖屏切换时悬浮窗布局错乱
1.1.3 多设备适配
1.1.3.1 悬浮窗多设备尺寸自适应适配

说明

根据断点布局实现悬浮窗的自适应:

横向断点 sm md lg xl
属性 底部导航栏,适配竖屏手机 侧边导航栏,适配横屏平板 底部导航栏,适配折叠屏 侧边导航栏,适配PC端
图片 图片 图片 图片
1.1.3.2 悬浮窗多设备窗口变化适配
  • 适配点1:基于断点实现组件自适应在多设备布局适配,窗口响应(横竖屏、分屏、折叠开合等)

图片 图片

  • 适配点2:组件尺寸自适应覆盖拉伸、均分、占比、缩放、延伸、隐藏、折行。

图片

1.2 开发指导

1.2.1 Flutter开发

1.2.1.1 关键能力

flutter中需要依赖原生接口enableLandscapeMultiWindow接口监听悬浮窗状态变化,从而自适应改变组件大小。

Dart侧:

final double desHeight = MediaQuery.of(context).size.height;
final bool isLandscape = message == '2';
final double itemHeight = desHeight * (isLandscape ? _Landscape : _original);

arkts侧:

setLandscapeMultiWindow(flag: boolean) {
  this.mainWindow1 = AppStorage.get<window.Window>('mainWindow1');
  if (flag) {
    this.mainWindow1!.enableLandscapeMultiWindow()
    .then(() => {
      this.landscapeState = 1;
      .catch((err: BusinessError) => {
        hilog.error(0x0000, 'WindowUtil', `Failed to set the orientation. Code: ${err.code}, message: ${err.message}`,
        JSON.stringify(err) ?? '');
      });
    } else {
      this.mainWindow1!.disableLandscapeMultiWindow()
      .then(() => {
        this.landscapeState = 2;
        .catch((err: BusinessError) => {
          hilog.error(0x0000, 'WindowUtil', `Failed to set the orientation. Code: ${err.code}, message: ${err.message}`,
          JSON.stringify(err) ?? '');
        });
      }
    }
  }
}

1.2.1.2 指导案例

flutter使用原生接口,需要通过 channel 调用。再根据悬浮窗状态变化,动态设置组件大小。

1.2.1.3 示例代码

Future<void> _setLandscapeMultiWindow() async {
  String getLandscapeMultiWindow;
  try {
    final result = await _platform.invokeMethod<int>('setLandscapeMultiWindow');
    getLandscapeMultiWindow = (result ?? 2).toString();
  } on PlatformException catch (e) {
    getLandscapeMultiWindow = "Failed to get LandscapeMultiWindow: '${e.message}'.";
  }
  setState(() {
    message = getLandscapeMultiWindow;
  });
}

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(binding.getBinaryMessenger(), "samples.flutter.dev/LandscapeMultiWindow");
  let that = this;
  this.channel.setMethodCallHandler({
    onMethodCall(call: MethodCall, result: MethodResult) {
      switch (call.method) {
        case "setLandscapeMultiWindow":
          that.api.getLandScapeState(result);
          result.success(true);
          break;
          default:
          result.notImplemented();
          break;
      }
    }
  })
}

getLandScapeState(result: MethodResult) {
  this.isLandScape = !this.isLandScape
  this.setLandscapeMultiWindow(this.isLandScape)
  result.success(this.landscapeState);
}

悬浮窗的Sample示例代码地址:flutter 悬浮窗,开发者可以通过该地址查看完整的示例代码,并根据自己的需求进行修改和扩展。


更多关于HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-悬浮窗场景的实战教程也可以访问 https://www.itying.com/category-92-b0.html

2 回复

在HarmonyOS Next中,Flutter悬浮窗需通过Platform Channel调用ArkTS侧API:使用window.createSubWindowwindow.createWindow,设置type: WindowType.TYPE_FLOAT,并申请ohos.permission.SYSTEM_FLOAT_WINDOW权限。Flutter侧定义MethodChannel发送显示、隐藏、位置更新等指令。需在UIAbility的onWindowStageCreate中创建窗口实例并绑定XComponent以嵌入Flutter渲染内容。

更多关于HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-悬浮窗场景的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在多设备悬浮窗场景中,Flutter 需要动态感知窗口尺寸、方向及多窗口状态。适配核心为:

  1. 断点自适应:通过 MediaQuery.of(context).size 获取当前宽度,匹配 sm/md/lg/xl 等断点,设定悬浮窗宽高及位置。
  2. 原生能力获取:通过 MethodChannel 调用 ArkTS 侧的 enableLandscapeMultiWindow / disableLandscapeMultiWindow 监听横竖屏及多窗口变化,获取 landscapeState,回传 Flutter 用于动态计算组件比例。
  3. 安全区域避让:使用 MediaQuery.of(context).padding 获取状态栏、导航栏区域,结合 Stack + Positioned 绝对定位时偏移相应距离,防止遮挡。
  4. 窗口变化响应:监听尺寸变化(分屏、折叠展开),触发 setState 按窗口宽高百分比重绘悬浮窗,避免折痕区域;在 PC/平板,将悬浮窗吸附至窗口边缘或侧边区域,保证可见性。

示例核心逻辑(Dart 侧):

final size = MediaQuery.of(context).size;
final padding = MediaQuery.of(context).padding;
bool isLandscape = message == '2'; // 来自 Channel
double width = size.width * (isLandscape ? 0.3 : 0.8);
double top = padding.top + 10;
// 在 build 中使用 Positioned 定位
回到顶部