上个月我把一个内部工具App往OpenHarmony上迁移,同事第一反应是:“ArkTS都封装好了,你还在折腾Flutter?”我的理由其实很简单——团队好几条产品线都是Flutter技术栈,与其到OpenHarmony上用ArkTS重写一套UI,不如先把Flutter渲染引擎在这个平台跑通,UI层直接复用。而验证这条路能不能走通,我选了一个最不起眼的组件开始:Checkbox复选框。
这个选择后来被证明是性价比最高的“探路石”。Checkbox看起来简单,实际涉及点击区域、状态刷新、主题配色、文字布局、父子交互(CheckboxListTile)一整套基础能力,在任何平台上能跑通,基本说明Flutter的交互链路在这个平台是通的。这篇文章我会完整记录我在OpenHarmony开发板上跑Flutter Checkbox的过程:环境怎么搭、设备树怎么选、组件内部怎么渲染的、属性怎么用,以及CheckboxListTile文字间距那些“翻车”细节怎么修。适合正在评估Flutter迁移OpenHarmony、或者已经在OpenHarmony上跑Flutter却被基础组件坑到的开发者参考。
1. 为什么拿Checkbox当“探路石”:Flutter上OpenHarmony的适配现状与选型判断
1.1 Flutter for OpenHarmony到底是什么
很多人一听“Flutter for OpenHarmony”以为是把Flutter重新实现了一遍,实际上它是一套移植适配方案:OpenHarmony社区(主要是开源社区里维护的openharmony-sig组织)维护了Flutter的OpenHarmony分支,把Flutter引擎的渲染后端、平台通道(platform channel)、光栅化管线接到OpenHarmony的图形栈和系统能力上,而Dart框架层、Widget层基本不用动。换句话说,你的Flutter代码在Android、iOS、Web上怎么写,在OpenHarmony上还怎么写,差异集中在底层引擎和少量平台适配。
这套方案的核心价值在于代码复用。我们团队多年积累的Flutter业务组件、状态管理方案、UI规范,不需要因为切换到OpenHarmony就推倒重来。特别是对纯UI展示、表单、列表这类业务,迁移成本几乎为零。Checkbox这种Material基础组件就属于典型的“零成本迁移”对象。
1.2 为什么偏偏选Checkbox做验证
在OpenHarmony上跑Flutter,最怕的不是复杂功能,反而是简单组件“阴沟翻船”。我选Checkbox跑通全流程,是因为它在组件体系里非常有代表性:
- 它包含完整的交互状态机:选中、未选中、禁用、三态(tristate),每一态都对应了RenderObject层面的重绘和语义节点更新,能验证Flutter的响应式状态刷新在OpenHarmony上是否正常。
- 它依赖主题系统和Material渲染:activeColor、fillColor、side、shape都取自ThemeData,跑通了说明Material主题解析没问题。
- 它有组合形态:CheckboxListTile把Checkbox和文字排版绑定在一起,能验证多组件布局在OpenHarmony上的表现。
- 它是纯Dart组件:不涉及任何原生插件调用,可以隔离变量——如果Checkbox都有问题,说明问题出在引擎层而不是平台能力层。
1.3 什么项目适合迁移、什么项目要慎重
不是所有Flutter项目都适合马上迁到OpenHarmony。拿我们的项目来说,纯业务逻辑多、原生依赖少的工具类应用迁移非常顺利,但如果你想迁的是一个重度依赖地图SDK、推送SDK、支付SDK的商业App,就得提前做一轮插件排查。我用一个表格说明我的判断标准:
| 迁移角度 | 建议 | 原因 |
|---|---|---|
| 纯Flutter UI/内部工具 | 可以直接上 | 基础组件、布局、状态管理基本零成本复用 |
| 依赖path_provider、shared_preferences等轻量插件 | 先验证再上 | OpenHarmony社区有对应适配,但版本可能滞后 |
| 依赖地图、支付、推送等厂商SDK | 慎重 | 需要厂商提供OpenHarmony版本,或者自己写platform channel做原生对接 |
| 涉及蓝牙、USB、串口等底层硬件 | 单独评估 | 这类能力和系统底层绑定,往往要基于OpenHarmony原生API重写,比如有人做USB通信就得对接usbmanager,无法直接用Android那套接口 |
对大多数团队来说,最稳妥的路径就是像我这样:先挑一个内部小工具或核心业务的一个页面迁移,组件从Checkbox这类基础控件开始跑通,再逐步扩大范围。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 工程环境搭建与设备树选择:RK3568/RK3588开发板实操避坑
2.1 环境清单与工具链版本
在OpenHarmony上跑Flutter,整个环境链比单纯Flutter开发要多一层。我的开发环境按下面这个清单准备:
- 一台Linux主机(Ubuntu 20.04/22.04都行),用来编译和构建,纯Dart侧的编译在Windows/macOS也可以,但涉及OpenHarmony SDK构建时Linux环境兼容性最好。
- DevEco Studio(OpenHarmony的IDE工具),用来配置OpenHarmony SDK路径、签名、调试。
- OpenHarmony SDK(通过DevEco Studio的SDK Manager下载),里面包含API、工具链和鸿蒙的编译依赖。
- Flutter SDK的OpenHarmony适配分支,这个通常托管在社区维护的flutter仓库里,按官方README拉取对应分支。因为适配分支更新节奏和主线不完全一致,我建议直接克隆社区维护的仓库而不是Flutter官方主线,否则构建时可能报SDK版本对不上。
- RK3568或RK3588开发板。网上搜“openharmony rk3568”“openharmony rk3588”的热度一直很高,说明这是目前最常见的验证平台。我自己用的是RK3568,8GB内存版本,跑起来流畅度可以接受。
环境变量配置时有一个容易忽略的点:ANDROID_HOME和OpenHarmony的SDK路径不要混用。如果你的机器上同时装了Android SDK(很多Flutter开发者都有),flutter doctor会优先识别Android工具链,OpenHarmony的构建脚本有时候也会去读ANDROID_HOME。我在配置时把Android SDK路径单独存放在ANDROID_SDK_ROOT,而ANDROID_HOME保持干净,避免两个SDK的gradle插件互相干扰。另外搜索引擎里那个“you are applying flutter's main gradle plugin imperatively using the apply script”的报错,其实就是SDK路径冲突导致的gradle插件应用方式问题,改一下环境变量基本能解决。
2.2 RK3568设备树到底怎么选
说到RK3568,很多第一次接触OpenHarmony的人会被“设备树”这件事劝退——同一颗RK3568芯片,为什么会有那么多设备树文件?到底选哪个?
设备树的本质是描述硬件配置的数据文件,告诉内核当前板子上有哪些设备、各自的中断号、寄存器地址、引脚复用关系。RK3568是一颗通用SoC,它被很多厂商拿去做不同的开发板:有的板子用的是MIPI屏幕,有的是HDMI输出;有的带WiFi模块,有的用以太网。同一个芯片,不同板卡的外设差异很大,所以必须用不同的设备树来区分。
那“到底咋选”?我踩过坑后的结论分两步:
- 先搞清楚你的开发板具体型号。比如你买的是某某科技的RK3568开发板,那就去这个厂商的资料包/镜像里找对应设备树。厂商出厂固件里已经打包好了正确配置,你直接烧录官方镜像即可,通常不需要手动选设备树。
- 如果你是自己在源码树里编译镜像,才需要手动指定设备树文件。OpenHarmony内核的设备树一般在
kernel/linux的arch/arm64/boot/dts/rockchip目录下,命名规则通常是rk3568-xxxx.dts,xxxx就是板卡或产品的名字。我用的板子是通用评估板,就选那个以evb开头的dts。
RK3588同理,只是性能更强、支持更多显示输出,适合做多屏交互类的原型验证;如果只是验证Flutter基础组件和引擎渲染,RK3568完全够用,性价比也高,网上资料还多。
2.3 依赖下载与热重载那些“小毛病”
在OpenHarmony上跑Flutter,有两个环境层面的高频问题,热搜里也经常出现:
第一个是依赖包下载慢/下载失败。Flutter的pub仓库和引擎产物都在海外,默认源在国内经常超时。解决办法是把PUB_HOSTED_URL和FLUTTER_STORAGE_BASE_URL指到国内镜像。网上能搜到那句“flutter assets will be downloaded from https://storage.flutter-io.cn”其实就是镜像环境变量已经生效了。这个属于基础操作,但很多人第一次配OpenHarmony环境时把注意力都放在SDK上,忘了配这两个变量,导致后续构建卡住。
第二个是热重载后页面没更新。Flutter传统优势是热重载(hot reload),但在OpenHarmony设备上有时改了代码点R,界面纹丝不动。这个不一定是引擎问题,我在开发板上的排查结果是:工程没有以debug模式启动,或者改动的代码不在当前运行库的编译范围内。最简单的办法是执行flutter clean后重新编译一次,再往后就恢复了。如果频繁遇到,检查一下是不是开了混淆或者构建模式设置成了release——release模式下本身就不支持热重载。
3. Checkbox的渲染链路:从Widget声明到OpenHarmony屏幕像素
3.1 一条Checkbox是怎么画出来的
很多写Flutter的人每天用Checkbox,但不太清楚它到底经历了什么才显示到屏幕上。我梳理一下这个链路,因为在OpenHarmony上排查问题时,这个认知帮了大忙。
你在代码里写的Checkbox(...)是一个Widget,它只是一个配置描述,本身不画任何东西。Flutter内部经过三层结构:
- Widget层:声明式的配置,描述“我想要一个什么样的复选框”;
- Element层:负责挂载和更新,把Widget配置和真正的渲染对象关联起来;
- RenderObject层:真正执行布局和绘制的地方。
Checkbox最终对应到一个RenderToggleable对象(内部包含绘制方框、对勾、水波纹的逻辑),它和兄弟组件Switch共用一套toggle渲染引擎。在渲染阶段,RenderToggleable把方框和勾的路径交给Flutter的绘制引擎,最终通过光栅化输出到屏幕。
在OpenHarmony上,链条的最后一段发生了变化:Flutter引擎输出的不是Android那套SurfaceFlinger接口,而是对接OpenHarmony的图形渲染能力,由社区适配层完成承接。不过对业务开发者来说,到RenderObject这一层就为止了,你不需要关心底层是哪个图形栈,只需要知道三层结构在OpenHarmony上是透明的就行。
3.2 Material组件在OpenHarmony上的“样子”
这里有个容易混淆的点。OpenHarmony原生开发用的是ArkTS和方舟UI框架,它自带一套HarmonyOS Design风格的复选框组件,和Flutter的Material Design风格是两套视觉语言。你如果在OpenHarmony应用里同时用了ArkTS原生页面和Flutter页面,会明显感觉到两个页面的控件风格不统一——除非你在Flutter侧把主题往鸿蒙风格上校准。
我实测下来,Flutter的Material Checkbox在OpenHarmony上的表现和Android端基本一致:水波纹、对勾动画、禁用态透明度都没有明显异常。所以如果你的产品视觉本身就以Material为基准,那在OpenHarmony上几乎不用改。如果产品必须严格遵循鸿蒙原生设计规范,那就得到主题层去定制,这个放到后文专门讲。
3.3 和ArkTS原生Checkbox的对比
做技术选型时,团队内部肯定会拿出原生Checkbox和Flutter Checkbox对比。我拉了下面这个表,方便你决策:
| 对比维度 | Flutter Checkbox | ArkTS原生Checkbox |
|---|---|---|
| 开发语言 | Dart | ArkTS/TypeScript |
| UI风格 | Material Design | HarmonyOS Design |
| 状态管理 | 父Widget + setState管理 | @State装饰器管理 |
| 样式定制 | ThemeData/CheckboxThemeData统一控制 | 通过属性+自定义样式 |
| 代码复用 | Android/iOS/Web/OpenHarmony跨端 | 仅OpenHarmony |
| 学习成本 | 需要了解Flutter生态 | 需要了解ArkTS生态 |
一个有意思的结论是:如果你本身已经有Flutter开发经验,用Flutter写Checkbox并不比用ArkTS原生写更复杂,甚至组件生态和开发效率更高。这正是“Flutter for OpenHarmony”这个方向能成立的核心逻辑。
4. Checkbox属性与三态实战:单选框、半选态、商品全选列表实现
4.1 最基础的Checkbox用法
先看一个最典型的场景:单个复选框,选中状态由StatefulWidget管理。
dart复制class SingleCheckboxPage extends StatefulWidget {
const SingleCheckboxPage({super.key});
@override
State<SingleCheckboxPage> createState() => _SingleCheckboxPageState();
}
class _SingleCheckboxPageState extends State<SingleCheckboxPage> {
bool _checked = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('基础复选框')),
body: Center(
child: Checkbox(
value: _checked,
onChanged: (bool? value) {
setState(() {
_checked = value ?? false;
});
},
),
),
);
}
}
这段代码的核心逻辑就一句话:Checkbox本身不存状态,状态永远在父级Widget里。onChanged回调里拿到新值后必须调setState触发重建,UI才会刷新。很多新手在这里犯的一个错误是以为勾选动作会自动改内部状态,其实Material规范要求复选框的选中态完全由开发者控制——这也是为什么Flutter允许多个Checkbox共享同一个值,因为状态值根本没有绑定在组件内部。
4.2 常用属性:不只是value和onChanged
Checkbox的参数比很多人想象的多,在OpenHarmony上做UI统一时会用到。我把常用的整理成表格:
| 参数 | 作用 | 我的使用建议 |
|---|---|---|
| value | 是否选中 | 必填,三态时传null |
| tristate | 是否启用半选态 | 做“全选/半选”功能时设为true |
| activeColor | 选中时的背景色 | 优先用主题色,不要写死值 |
| checkColor | 对勾的颜色 | 一般白色,和activeColor形成对比 |
| fillColor | 各状态下的填充色 | 用MaterialStateProperty按状态区分 |
| side | 边框样式 | 用BorderSide控制宽度和颜色 |
| shape | 形状 | 默认圆角矩形,可改成圆形等 |
| overlayColor | 点击水波纹颜色 | 产品和视觉规范如果严格可以自定义 |
| materialTapTargetSize | 点击区域大小 | 在OpenHarmony上建议padded |
| visualDensity | 视觉密度 | 调紧凑/宽松布局时使用 |
这里尤其提一下fillColor。它接受的是MaterialStateProperty,可以按selected、disabled、hovered等状态分别给颜色。如果你希望复选框在OpenHarmony上更贴合鸿蒙那种“选中即强调色”的感观,可以定义这样一套状态色:
dart复制Checkbox(
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return Colors.grey.shade300;
}
if (states.contains(MaterialState.selected)) {
return Theme.of(context).colorScheme.primary;
}
return Colors.transparent;
}),
)
4.3 三态复选框:商品列表全选功能
Checkbox最经典的三态应用是“全选/半选”。tristate设为true后,value可以接收三种状态:true表示全选、false表示全部不选、null表示部分选中。
我拿一个电商订单页举例:顶部有个“全选”Checkbox,下面是一组商品条目,每个条目前也有一个Checkbox。它们的逻辑是:
- 商品全部选中,顶部的“全选”显示勾选;
- 商品一个都没选,显示未勾选;
- 部分选中,显示半选状态。
完整实现可以这样写:
dart复制class SelectAllExample extends StatefulWidget {
const SelectAllExample({super.key});
@override
State<SelectAllExample> createState() => _SelectAllExampleState();
}
class _SelectAllExampleState extends State<SelectAllExample> {
final List<bool> _items = List.generate(5, (_) => false);
bool get _isAllChecked => _items.every((checked) => checked);
bool get _isSomeChecked => _items.any((checked) => checked) && !_isAllChecked;
void _toggleAll(bool? value) {
setState(() {
for (int i = 0; i < _items.length; i++) {
_items[i] = value ?? false;
}
});
}
void _toggleItem(int index, bool? value) {
setState(() {
_items[index] = value ?? false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('全选半选示例')),
body: Column(
children: [
CheckboxListTile(
title: const Text('全选'),
value: _isAllChecked,
tristate: _isSomeChecked,
onChanged: _toggleAll,
),
const Divider(),
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text('商品 $index'),
value: _items[index],
onChanged: (value) => _toggleItem(index, value),
);
},
),
),
],
),
);
}
}
注意我在顶部CheckboxListTile上用了tristate: _isSomeChecked这种写法:当部分选中时,tristate为true且value为false,组件就会渲染出半选状态的横线。这是一个比较实用的小技巧。你要在OpenHarmony上验收半选渲染是否正确,就看方框里是不是出现了一条短横线,以及点击后是否按“半选→全选”的方向切换。
5. CheckboxListTile排版翻车实录:文字间距问题的定位与修复
5.1 问题描述:文字紧贴复选框,怎么看怎么别扭
在我们迁移的第一个正式页面——一个设置中心——就遇到了网上很多人都在搜的问题:CheckboxListTile的文字离复选框太近。我当时以为OpenHarmony的字体渲染或者屏幕密度导致了这个间距异常,因为同页面在Android模拟器上看起来正常,到了开发板上就不对劲了。
最开始我怀疑是Dart侧的主题字体族问题。OpenHarmony的系统字体和Android不一样,它的默认字体可能让文字宽度计算偏大或偏小,导致ListTile内部的文字和控件之间的间距感知发生变化。但后来我把字体显式设置成和Android端一样的字体后,间距问题依然存在——这说明问题不是字体,而是布局参数。
5.2 排查过程:ListTile的默认密度和内容边距
CheckboxListTile本质上是ListTile套了一个Checkbox。ListTile内部有自己的一套布局参数,其中最影响间距的是两个:
visualDensity:视觉密度,默认值在Material规范里是VisualDensity.adaptivePlatformDensity,会根据平台和触摸目标尺寸调整。在桌面端和移动端、不同分辨率屏幕上,计算出来的密集程度不一样。contentPadding:内容内边距,默认是EdgeInsets.symmetric(horizontal: 16),但它在不同的density和tile场景下可能被内部逻辑覆盖。
我通过控制变量法逐步排查:先把CheckboxListTile的dense设为false/true对比,发现间距变化不明显;接着把visualDensity分别设为comfortable、compact和自定义值,间距终于出现了明显变化。最后再检查contentPadding,发现它在某些平台视觉密度下确实会被压缩,导致文字和复选框的距离不满足常规视觉预期。
5.3 修复方案:直接调视觉密度和内边距
修复方案不复杂,核心是显式覆盖默认参数,不让平台自适应密度影响布局。我最终在设置中心页面统一用了这样一个封装:
dart复制class CustomCheckboxListTile extends StatelessWidget {
const CustomCheckboxListTile({
super.key,
required this.title,
required this.value,
required this.onChanged,
});
final String title;
final bool value;
final ValueChanged<bool?> onChanged;
@override
Widget build(BuildContext context) {
return CheckboxListTile(
title: Text(title),
value: value,
onChanged: onChanged,
controlAffinity: ListTileControlAffinity.leading,
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
activeColor: Theme.of(context).colorScheme.primary,
);
}
}
这里三个关键点:
contentPadding显式设置成较紧凑的8像素水平内边距,不再依赖平台默认值;visualDensity直接给一个固定的水平、垂直密度值,避免自适应密度在不同设备上表现不一致;controlAffinity明确设为leading,让复选框始终在左侧、文字在右侧,不给平台策略留解释空间。
运行到开发板上一看,间距终于正常了。
5.4 顺带踩掉的另一个坑:点击区域偏小
和文字间距问题一起出现的还有点击区域问题。在OpenHarmony上,由于系统对触摸目标尺寸的要求和Android/Linux桌面不完全一样,默认的materialTapTargetSize在开发板上会让复选框的可点击区域偏小,用户体验有明显“点不准”的感觉。
解决办法是给Checkbox或CheckboxListTile包一层Material并显式设置materialTapTargetSize: MaterialTapTargetSize.padded,或者用IconButton的方式扩大点击范围。我更推荐前一种,因为padded是Material规范里保证至少48x48dp触摸区域的机制,能一次性解决OpenHarmony真机上“误触”和“点不中”两个问题。
6. 主题定制与无障碍补全:让复选框长成鸿蒙应用该有的样子
6.1 全局统一Checkbox主题
产品视觉由Material默认的紫色换成主色蓝之后,如果每个Checkbox都手动传activeColor,页面一多就失控了。更好的做法是在ThemeData里统一配置checkboxTheme。我以OpenHarmony上的实际工程为例:
dart复制ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF007DFF)),
checkboxTheme: CheckboxThemeData(
fillColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return Colors.grey.shade400;
}
if (states.contains(WidgetState.selected)) {
return const Color(0xFF007DFF);
}
return Colors.transparent;
}),
checkColor: const WidgetStatePropertyAll(Colors.white),
side: const BorderSide(width: 1.5),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
overlayColor: WidgetStatePropertyAll(
const Color(0xFF007DFF).withOpacity(0.08),
),
materialTapTargetSize: MaterialTapTargetSize.padded,
),
)
我把fillColor和shape、side都统一到主题层,这样全页面所有Checkbox的视觉自动一致。将来要全套换肤,也不用改每一个页面。尤其注意我在checkColor上用了WidgetStatePropertyAll,这个写法比直接传一个Color更符合新版API的更新趋势——旧版checkColor: Colors.white在某些版本上会提示deprecated。
一个从热搜里看到的高频问题:flutter showlicensepage 页面的主题颜色。这个坑和Checkbox主题其实是同一类问题:showLicensePage()在OpenHarmony上弹出时,用的不是App根MaterialApp的主题,而是页面构造时临时生成的默认主题,导致弹出的License页和App风格不一致。解决办法是不要直接调showLicensePage的默认构造,而是在调之前给Theme包一层:
dart复制showLicensePage(
context: context,
applicationName: '我的应用',
applicationVersion: '1.0.0',
applicationIcon: const FlutterLogo(size: 56),
theme: Theme.of(context),
);
把theme参数显式传成当前页面的主题,弹出的License页就正常了。这个技巧在OpenHarmony上特别实用,因为showLicensePage内部对默认主题的依赖很强,而OpenHarmony的Material默认主题和App定制主题差距又大,不传theme几乎必翻车。
6.2 无障碍语义补全
OpenHarmony的屏幕阅读器对Flutter引擎的支持目前还在完善阶段,测试时我发现部分语义节点不能被无障碍服务正确识别。具体表现是:鼠标键盘操作Checkbox完全正常,但开启TalkBack类的屏幕朗读服务后,读屏焦点可能在文字上,却读不到“复选框”这个控件角色,或者读不出勾选状态。
Flutter本身的解决方案是Semantics组件,它可以在不改变视觉的前提下,主动声明节点的语义信息。我给项目里的CheckboxListTile补了这样一层:
dart复制Semantics(
label: '商品 $index',
hint: '双击切换选中状态',
checked: _items[index],
child: CheckboxListTile(
title: Text('商品 $index'),
value: _items[index],
onChanged: (value) => _toggleItem(index, value),
),
)
注意check属性在语义树中表达的是“勾选状态”,不是“按钮是否可用”,很多文档把它写成checked,在Semantics组件里也确实有这个参数。加上之后,在开发板上配合系统读屏服务测试,读屏能正确播报“商品1,复选框,已选中”之类的信息。做无障碍适配时,一定要在真机上联调读屏服务,模拟器上的表现和真机差异很大,OpenHarmony开发板尤其如此。
6.3 迁移后的实测总结与建议
项目里跑通Checkbox之后,我又陆续迁移了TextField、Switch、Radio等基础组件,整体结论是:Flutter在OpenHarmony上的基础组件链路已经具备可用性,只要你不踩到平台适配的暗坑(密度、内边距、主题默认值),开发体验和Android端差别不大。
最后分享一个我从这次迁移里得到的最有价值的经验:在OpenHarmony设备上调试Flutter UI,不要一上来就用真机烧录跑全流程,效率太低。我的习惯是先拿Flutter桌面/Web端做快速验证,把布局、视觉密度、交互逻辑都调好了,再同步到OpenHarmony开发板上做真机验收。一来开发板上的编译和部署周期比桌面端长不少,二来OpenHarmony上能用的调试工具还不够丰富,桌面/Web端能更快帮你定位到底是UI逻辑问题还是平台适配问题。Checkbox这次遇到的间距问题,我其实是在桌面上先复现、再上板子确认根因的,排查效率比直接对开发板快很多。
如果你正打算把Flutter项目迁到OpenHarmony,或者刚在开发板上敲下第一行flutter create,我建议你也找一个像Checkbox这样不起眼但五脏俱全的基础组件入手,先跑通一个最小闭环。等你亲眼看到那一个小小的方框在开发板上被勾选、取消、半选、禁用,你就知道Flutter在OpenHarmony上的路,已经能走通了。
