手机好用不代表折叠屏好用:Flutter 鸿蒙应用的多设备验收矩阵

应援灯牌编辑页在直板手机上看起来完整:输入文案,调颜色,预览效果,再发送到设备。换到展开态折叠屏,所有控件仍挤在屏幕中央;切到悬停态,预览区被折痕切开;从横屏回竖屏,编辑内容还丢了。这些问题不是“屏幕变大了,多留一点白”那么简单,而是布局、状态、输入方式和窗口生命周期同时发生了变化。

在这里插入图片描述

本文把跨平台应援灯牌控制应用的多设备适配拆成“可安装、可布局、可操作、状态连续、能力可用”五层。参考 Flutter/OpenHarmony 基础工程的页面使用了 SafeArea 与 SingleChildScrollView,但没有针对平板或折叠屏的布局断点,也没有多形态设备实测记录。下面的宽屏代码是面向灯牌编辑页的设计示例,不能当作已集成的功能。

1. 设备适配先从业务任务而非设备名称出发

灯牌应用最重要的任务是“编辑 → 预览 → 连接/发送”。手机竖屏适合线性流程,大屏可把编辑与预览并列,但业务状态必须是同一份。若只把控件拉宽,大屏虽然没有溢出,用户仍要在过长的输入栏和空白画布之间来回移动;若为了双列布局重新创建两套编辑器,则窗口缩放时文案、颜色和速度可能重置。

空间首要目标容易遗漏
手机窄屏输入、关键按钮可见,必要内容能滚动软键盘遮挡提交按钮
折叠屏展开编辑和预览利用宽度,但操作顺序清晰折痕/悬停态分割内容
平板/自由窗口列宽受控,预览不被无限拉伸窗口缩小时状态丢失
横屏短高关键操作可触达顶部栏与键盘占尽高度

官方 多设备通用适配指南 也强调随窗口空间动态调整布局,并在形态与窗口状态切换时验证体验连续。这里讨论的是方法,不表示某个 Flutter OH 版本已经支持所有 HarmonyOS 设备形态。

在这里插入图片描述

2. 当前基础页面已经解决了什么

参考工程首页的主体使用 SafeArea、滚动容器和 24 像素边距。对普通手机来说,它至少避免内容直接顶到系统安全区域,并为短屏提供纵向滚动。这是基础保障,不是完整的一多适配。

body: SafeArea(
  child: Center(
    child: SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [/* 当前首页内容 */],
      ),
    ),
  ),
)

这段代码所在页面只是欢迎信息示例,并非真实灯牌编辑器。它没有 LayoutBuilder 或窗口宽度分支;因此不能根据“手机上不溢出”推断“平板布局已经完成”。读者在自己的项目里应先找出核心页面的输入区、预览区和连接操作区,再决定宽屏时哪些区域并列。

在这里插入图片描述

3. 断点应该由内容需要决定

Flutter 中可用 LayoutBuilder 观察当前内容区域的约束,而不是只读取设备型号。以下示例把编辑面板和灯牌预览作为两个已有子组件传入;720 是示意断点,需要按实际最小可读宽度调整,不是 HarmonyOS 的固定标准。

class LedEditorLayout extends StatelessWidget {
  const LedEditorLayout({
    super.key,
    required this.editor,
    required this.preview,
  });

  final Widget editor;
  final Widget preview;

  
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      if (constraints.maxWidth < 720) {
        return Column(children: [editor, preview]);
      }
      return Row(children: [
        Expanded(flex: 2, child: editor),
        const SizedBox(width: 24),
        Expanded(flex: 3, child: preview),
      ]);
    });
  }
}

这个组件只决定排布,不拥有文案、颜色和速度状态。这样窗口在断点附近来回变化时,页面不会因为创建新的编辑状态对象而丢输入。实际落地还要给预览区限制纵横比和最大高度,避免灯牌比例被大屏强行拉坏;预览所需比例来自业务设计,而不是跟随屏幕比例。

4. 状态要放在布局分支之外

若窄屏用 MobileEditor()、宽屏用 TabletEditor(),而两者各自持有 TextEditingController,切换窗口宽度时很容易出现输入内容回到初始值。更稳妥的是在上层持有编辑状态,让不同布局消费同一份值。下面是一个最小状态边界示意:

class LedDraft {
  const LedDraft({
    required this.text,
    required this.color,
    required this.speed,
  });

  final String text;
  final Color color;
  final double speed;
}

// 父级 State 持有 draft;布局组件只接收状态与回调。

状态模型要覆盖真正会影响预览与设备输出的字段。灯牌文案、颜色和速度不应只藏在某个 TextField 内;相机或照片导入等异步状态还要在切换窗口后保持加载/失败信息。至于是否写入本地存储,是另一层持久化决策;“切换布局不丢值”不等于“杀进程后也不丢”。

5. 折叠/悬停态不是只看总宽度

展开态可能很宽,悬停态却有不可用的折痕区域。若预览图跨越折痕,用户很难判断灯牌的真实效果;若操作按钮恰好压在折痕上,就算布局没有 overflow 也不能算可用。官方 折叠屏适配指南 强调形态切换时内容连续和避免预览拉伸、裁剪等问题。

Flutter 层首先应验证窗口尺寸变化能重排;遇到需要识别具体折叠形态或折痕区域的场景,再核对所选 Flutter OH 版本是否提供对应平台信息或桥接能力。不要虚构一个通用的 Flutter isFolded API;不同宿主版本和插件实现可能不同。

折叠 → 展开:编辑状态保留,预览从下方移到右侧
展开 → 悬停:预览不跨折痕,关键操作区仍可触达
悬停 → 折叠:连接状态不因窗口重建被误判为断线

这些是验收断言,不是已经在参考工程中的实现记录。

6. 安装范围与页面布局是两件事

即使 Flutter 页面写了响应式布局,目标设备也可能因为产品配置、模块 deviceTypes、SDK 版本或签名而无法安装。参考工程 module.json5 的 deviceTypes 当前只有 "default";从这一个字段不能推断“手机、平板、鸿蒙电脑均已支持”,还要按目标平台文档核对具体设备类型、产品配置和渠道声明。

// 参考工程现状,不代表多设备发布配置
{
  "module": {
    "name": "entry",
    "deviceTypes": ["default"]
  }
}

验证应先问“包能不能装”,再问“页面能不能排”,最后问“业务任务能不能完成”。把安装失败和布局失败混为一类,会让问题错误地落到 Flutter UI 开发者身上。

7. 用窗口尺寸测试发现基本布局回归

自动化测试可以覆盖几个代表性窗口尺寸,帮助发现窄屏溢出或布局切换丢值。下面是 Flutter widget 测试思路,具体组件名由读者工程替换;测试仍需真实设备补足折痕、软键盘与平台能力。

testWidgets('led editor keeps text across width changes', (tester) async {
  await tester.binding.setSurfaceSize(const Size(390, 844));
  await tester.pumpWidget(const MaterialApp(home: LedEditorPage()));
  await tester.enterText(find.byKey(const Key('ledText')), '演唱会见');

  await tester.binding.setSurfaceSize(const Size(900, 600));
  await tester.pumpAndSettle();
  expect(find.text('演唱会见'), findsOneWidget);
});

这段是待集成的测试示意;参考基础工程没有 LedEditorPage。测试要在实际页面键值、状态管理和窗口约束落地后再运行。即使断言通过,也只说明测试环境下值未丢失,不证明折叠屏上预览比例正确或外设连接仍然稳定。

8. 实机矩阵要覆盖状态切换,不只是静态截图

一个设备截一张首屏图,只能回答某个瞬间有没有明显错位。多设备验收至少要包含连续动作:输入 → 开键盘 → 收键盘 → 旋转 → 调窗口 → 折叠/展开 → 返回应用。每一步记录文案、颜色、速度、预览和连接状态是否一致。

场景应保持的业务状态应观察的 UI
手机竖屏键盘弹出已输入文案提交按钮可达,输入框不遮挡
横屏短高当前颜色与速度工具栏不压预览,必要内容可滚动
折叠 → 展开文案/预览一致两列切换,避免突然空白
展开 → 悬停连接/播放状态预览避开折痕和不可触区
平板自由窗口缩小编辑草稿列布局退回单列,无溢出

官方 多设备适配总览 建议模拟器与远程真机结合;其中相机、窗口模式等差异还需按功能特殊处理。设备矩阵应把机型、系统版本、窗口尺寸和包哈希一起保存,否则同一个问题很难复现。

9. 故障定位:布局问题还是平台问题

现象先查
窄屏按钮不可见滚动、键盘避让、内容最小高度
宽屏预览过大预览的纵横比与最大约束
窗口变化后草稿消失状态是否藏在被替换的子组件内
折叠态点击无响应折痕/不可触区域与操作区位置
页面排版正常但无法装包deviceTypes、产品、SDK、签名
能装能看但外设不工作平台插件、权限和设备连接链路

尤其注意最后两行:多设备兼容不能只由 UI 截图证明。设备形态、原生能力和构建配置是并列的证据维度。

10. 文章给读者的验收清单

  1. 明确核心业务任务和每种空间下的信息层级,不只是增加留白。
  2. 文案、颜色、速度与预览状态由布局分支之外的层持有。
  3. 窄屏、短高、宽屏、自由窗口分别有布局与键盘检查。
  4. 折叠、展开、悬停转换时状态连续,预览不跨不可用区域。
  5. 安装范围、页面布局和外设能力分别记录结果。
  6. 自动化窗口测试与真实设备体验分别标注,不互相替代。

多设备适配的关键不是识别“这是一台平板”,而是识别当前可用空间与用户正在做的事,让输入、预览和设备控制在空间变化时保持同一条任务线。等真实 Flutter OH 目标设备支持关系确认后,再按这份矩阵逐一验收,才有资格说应用适配了多形态设备。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐