ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flutter在OpenHarmony上的工具卡片组件实战:文件转换助手开发指南

Flutter在OpenHarmony上的工具卡片组件实战:文件转换助手开发指南 1. 为什么我坚持用Flutter去趟OpenHarmony这趟水1.1 从一个跑不起来的Hello World说起先交代一下背景。我手上拿到一台OpenHarmony开发板系统版本还比较早期应用商店生态基本等于零想在上面做点工具类App第一反应当然是找现成的方案。OpenHarmony官方主推的是ArkTS ArkUI这套自研技术栈但我是从Flutter转过来的Dart写起来顺手而且手头已经有一堆Flutter组件和业务逻辑直接放弃重写成本太高。于是我把目光放在Flutter对OpenHarmony的适配方案上。稍作调研就会发现OpenHarmony虽然不能直接用官方Flutter SDK跑但社区维护的OpenHarmony分支已经存在一段时间了。我当时的心态是与其在ArkTS里重新学一套UI框架不如先把Flutter跑到OpenHarmony设备上解决“能不能跑”的问题再谈“跑得好不好”。实际动手时第一个坑就来了。用OpenHarmony分支的Flutter SDK创建工程之后编译时提示找不到Dart VM的初始化入口。日志里出现了类似error:flutter/runtime/dart_vm_initializer.cc(41)这样的内容当时就被卡住了半天。后来排查下来是工程里Flutter engine的版本和设备端系统版本不匹配OpenHarmony对Flutter引擎的接口兼容还没有做到像Android那样稳定。解决方式倒也直接把SDK切到和系统版本配套的分支重新flutter clean再编译问题消失。1.2 跨平台框架在OpenHarmony上的现状聊下我对当前生态的判断。OpenHarmony如果想快速拥有应用供给靠ArkTS原生开发短期内追不上Android和iOS的存量生态所以跨平台方案必然有一席之地。我在实际项目里验证过React Native和Flutter两条路RN在OpenHarmony上的适配更残缺很多基础组件还要自己补Flutter反而因为渲染引擎自绘的原理在系统控件依赖上更少迁移阻力相对小。但这里要明确一件事Flutter跑在OpenHarmony上不等于所有Flutter插件都能直接用。很多插件底层依赖Android系统的API比如文件选择器、分享面板、相机调用到了OpenHarmony上可能直接报MissingPluginException。所以在项目规划时我把依赖整理成三类纯Dart逻辑的库可以放心用涉及文件系统的库基本要换涉及系统能力调用的库大概率需要自己写MethodChannel桥接。从这个角度看文件转换助手这类工具App反而是最合适试水的项目类型。它不依赖复杂的原生UI组件核心就是文件读写、格式转换、状态展示这些能力通过Dart层和少量平台通道就能覆盖非常适合用来验证Flutter在OpenHarmony上的真实可行性。1.3 为什么选择“文件转换助手”作为实战题材我选择做文件转换助手有几个很现实的理由。首先是业务链路清晰用户选文件、选目标格式、点转换、看结果整个流程可以用卡片组件的思路来组织每一屏都可以抽象成一张独立的工具卡片正好呼应项目标题里的“工具卡片组件”这个关键词。其次是文件转换在工具类App里属于高频需求。图片转格式、文本转PDF、Markdown转HTML这些操作不需要太重度的原生能力核心逻辑用Dart基本可以写比如文本编码转换、JSON格式化、Base64编解码这类纯Dart就能搞定。真正依赖平台能力的只有文件读取和保存这两个点我在后面的章节会专门讲如何和OpenHarmony的平台通道对接。最后是验证目的明确。我的目标不是做一个功能完整到可以上商城的App而是验证三个问题Flutter在OpenHarmony上的渲染稳定性、插件替代方案是否可行、卡片式UI在这种设备上的体验如何。文件转换助手恰好能把这三个问题全部覆盖到。2. 开发环境准备把Flutter跑到OpenHarmony设备上2.1 选对Flutter SDK分支是第一步这一步我踩的坑最多专门拿出来说。OpenHarmony官方文档和社区仓库里维护的Flutter SDK分支有好几条命名方式类似于flutter_flutter和flutter_flutter_ohos的区别版本节奏和上游并不完全同步。如果你直接去clone官方Flutter仓库再切到OpenHarmony分支大概率会编译失败因为很多依赖路径写死在配置里。我推荐的做法是直接使用社区维护者发布的Release包或tag不要去碰master分支。我当时用的稳定版本和OpenHarmony系统的API版本需要一一对应比如OpenHarmony 4.0对应某个Flutter提交点OpenHarmony 4.1又对应另一个。确认版本匹配的方法也简单到设备上跑一个最简单的flutter run能出红色计数器的demo就说明SDK对齐了出不来优先怀疑分支版本错了。环境变量也要配置好。OpenHarmony的SDK路径要单独设置不能复用Android SDK的ANDROID_HOME我当时还遇到过一个奇怪的现象Flutter工具链去检测OpenHarmony SDK的时候会把它当成Android工程来解析导致build.gradle匹配失败。解决办法是把OpenHarmony SDK和Flutter SDK包在同一个工具链目录下并且在项目级的local.properties里显式写好SDK路径。2.2 IDE、模拟器与真机的调试选择开发工具上我主力用的是DevEco Studio配OpenHarmony插件但仅用来做工程管理和签名打包。日常写Dart代码还是在VS Code里完成两个IDE混用。这里提醒一句如果你只装了DevEco Studio它默认打开的工程类型是ArkTS工程需要手动把Flutter工程导入而且让DevEco去识别pubspec.yaml偶尔会失败。调试设备方面模拟器跑起来很慢而且OpenHarmony官方模拟器对Flutter的图形渲染支持还不太完善画面渲染经常出现撕裂。我的实际经验是准备一块真实的开发板通过USB连接调试效率比模拟器高很多。真机调试时用flutter run命令直接安装到设备首次安装速度较慢但后续热重载都能正常工作说明Flutter的Dart虚拟机在OpenHarmony适配层做得还算扎实Hot Reload这条链路没有被砍掉。还有一个细节OpenHarmony设备默认不开开发者模式需要连续点击版本号触发。USB调试模式的入口和Android不太一样藏在设置的安全选项里我第一次找了好久。开启后不要忘了在系统设置里允许安装未知来源应用否则flutter run会卡在安装阶段一直报权限错误。2.3 签名、打包与安装的基础流程OpenHarmony的应用签名机制比Android严格没有签名的应用在真机上装不上去。你需要自己在华为开发者联盟或者OpenHarmony的签名工具里生成证书文件。流程大概是这样创建一个调试证书把证书指纹填到工程的build-profile.json5里然后用签名工具生成.p7b签名文件。签名配置完成后flutter build产物是一个HAP包和Android的APK不同。我一开始习惯性地去找APK文件结果发现产物目录下只有一个没见过的后缀折腾了一会儿才意识到要打包成HAP才能装到OpenHarmony设备上。用hdc install命令安装hdc相当于adb在OpenHarmony上的版本命令参数几乎一样上手没什么成本。签名这块是新手最容易放弃的地方因为错误提示不够友好。我遇到最典型的是“Signature verification failed”但你根本不知道是证书失效还是指纹对不上。后面我总结了一个稳定的做法只用一个签名字段把自动签名的开关全部关掉手动指定签名文件路径反而比自动签名流程更可靠。3. 文件转换助手App整体架构设计3.1 核心链路与模块划分文件转换助手的业务链路不复杂但代码组织上如果处理不好后面加功能会很痛苦。我按以下方式划分模块文件解析层负责根据扩展名和MIME类型识别文件格式转换引擎层纯Dart逻辑存放各种格式转换的具体实现平台能力层通过MethodChannel调用OpenHarmony的文件的读取、保存、分享能力UI组件层也就是标题中重点说的工具卡片组件体系状态管理层用Provider管理转换任务列表和进度数据核心链路是这样的用户从文件源卡片选择文件文件解析层识别格式之后转换选项卡片动态生成可用的输出格式列表用户点确认后转换引擎层创建任务任务进度卡片实时刷新进度结果卡片展示输出文件并提供保存分享入口。这套拆分方式的好处是每一层都能独立测试。我在开发过程中经常是先把转换引擎的纯Dart逻辑用单元测试跑通再接UI层最后接平台通道。这样逐层推进出了问题不需要在UI和平台代码之间来回猜。3.2 为什么把UI重心放在“卡片”上标题里特别提到了工具卡片组件这个设计不是拍脑袋。工具类App和内容类App的UI逻辑不一样内容类需要大信息量列表而工具类每个操作步骤的信息密度都低用户需要的是“当前这一步该干什么”的清晰引导。卡片式布局天然适合工具类场景。它把一次转换任务拆成几个独立的视觉块文件源是一张卡片格式选择是一张卡片转换进度是另一张卡片。每个卡片只承担一个职责状态变化时只需要刷新这张卡片对应的Widget子树不需要整页重绘。更重要的是OpenHarmony设备的屏幕尺寸差异很大有手机也有平板还有跑在开发板上的小屏卡片加上灵活的网格布局可以用同一套代码适配这几个尺寸段。我实测过几种布局方案包括传统的ListView平铺和Tab切换最后都因为信息层级不清晰放弃了。卡片最大优势在于它能利用视觉边界表达任务状态比如正在转换的卡片高亮边框、完成的卡片显示勾选标记、失败的卡片变红提示用户扫一眼就知道当前的整体状态。3.3Provider状态管理在卡片体系中的定位Flutter状态管理的选择很多Bloc、Riverpod、GetX各有拥趸。我在这个项目里用Provider理由很简单状态树复杂度还没到需要Bloc引入事件流的地步而Provider作为InheritedWidget的上层封装在卡片刷新场景下的性能表现足够好。具体到工具卡片的场景状态分两种类型。一种是组件内部状态比如卡片展开还是收起、拖拽文件时的高亮状态这些直接用StatefulWidget管理就行不需要全局状态。另一种是跨卡片的共享状态比如文件源卡片选完文件后转换选项卡片要立刻感知并生成格式列表这时候就不能用局部State了。我把跨卡片状态抽成了一个ConversionTaskModel挂在Provider的最上层。文件源卡片读取文件后更新model里的文件路径字段转换选项卡片监听这个字段的变化来决定是否启用按钮。任务进度卡片则从model里读取每个转换子任务的状态流。这样各个卡片之间没有直接耦合都只和model通信后续往里面加新的卡片类型也只需要扩展model。这里再补充一个容易忽略的性能细节。Provider如果粒度太粗一个状态变化会触发多个卡片重建OpenHarmony上Flutter的渲染性能本来就不如Android原生重建范围一大就容易掉帧。我的做法是拆了多个Provider文件信息、格式选项、进度数据各管各的只让需要刷新的卡片订阅对应的Provider。3.4 转换任务的并行与状态流转设计文件转换不总是单个文件操作用户可能在文件源卡片里一次勾选了多个文件所以任务队列要考虑并发度。我实现的转换引擎里设计了一个简单的线程池机制最大并发数定为3每个转换任务在独立Isolate中运行避免大量文件转换时阻塞UI线程。状态流转上每个任务经历待处理、转换中、已完成、失败四个阶段。待处理状态在队列里排队卡片上显示等待图标转换中状态显示进度条已完成状态显示文件大小和保存按钮失败状态显示错误信息并提供重试入口。这个流转逻辑我写成了一个枚举类和对应的状态机切状态的时候统一走一个updateTaskStatus方法方便记录日志。这里要特别说下Isolate的使用。Dart的Isolate之间默认不共享内存只能通过消息传递数据但OpenHarmony的Flutter分支上Isolate的spawn能力我一度担心兼容性有问题实测下来居然很稳看来这部分引擎适配做得比较完整。4. 工具卡片组件拆解与实现4.1 可复用的卡片基类设计工具卡片组件要支撑文件源、格式选项、进度、结果预览这几种业务卡片第一件事是抽象出一个公共的卡片容器。这个容器不关心卡片里面具体是什么内容只管卡片整体的视觉样式、进入动画和基础交互行为。卡片基类的代码大概是这样的思路class ToolCard extends StatelessWidget { final Widget child; final VoidCallback? onTap; final bool highlighted; final EdgeInsets padding; const ToolCard({ super.key, required this.child, this.onTap, this.highlighted false, this.padding const EdgeInsets.all(16), }); override Widget build(BuildContext context) { return AnimatedContainer( duration: const Duration(milliseconds: 200), curve: Curves.easeOut, margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), padding: padding, decoration: BoxDecoration( color: highlighted ? Theme.of(context).colorScheme.primaryContainer : Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(16), border: highlighted ? Border.all( width: 2, color: Theme.of(context).colorScheme.primary, ) : null, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: child, ); } }这段代码的核心价值在于统一了卡片的高亮态和普通态切换逻辑业务卡片不需要各自处理边框、阴影和动画。我在后续开发中经常需要调整卡片间距因为OpenHarmony上不同设备的屏幕密度差异比较大模拟器和真机的视觉效果差别很明显最后干脆把间距参数全部提取成主题常量适配起来省了不少事。另外卡片基类里的onTap回调用了最简单的VoidCallback不搞复杂的GestureDetector嵌套。之前我试过在卡片里放一个可点击的子组件同时给整张卡片注册点击事件触发了手势冲突子组件点击会先触发父级的手势识别然后被取消表现就是按钮点了没反应。这个问题的解决方式很简单给子组件加一个AbsorbPointer包住让事件不再向父级冒泡。4.2 文件源卡片本地文件读取与展示文件源卡片是用户进入App后看到的第一张卡片也是后续所有流程的数据源头。功能上需要支持两种选文件方式从系统文件管理器里选择一个文件或者在卡片内直接浏览OpenHarmony设备存储里的常见目录。从系统文件管理器选择文件需要调用OpenHarmony的文件选择能力这里走MethodChannel桥接到原生侧。选完文件后文件信息通过回调传回Dart层文件源卡片负责展示文件名、文件大小和文件图标。文件图标这块我要提个醒不要自己去实现一套MIME类型到图标的映射表太容易出边界情况了。我验证过的大多数框架都把常见格式覆盖得比较好如果遇到一个不认识的扩展名就显示一个通用文件图标用户不会觉得有问题。真正要花心思的是文件重名的处理因为OpenHarmony设备上文件管理器的换名机制不统一我在代码里做了去重逻辑如果选中的文件在输出目录下已有同名文件就在文件名后面拼上时间戳。卡片上的文件列表还支持多选这块用了一个简单的选择状态集合来管理勾选状态。需要额外说明的是多选文件后卡片上需要显示一个汇总信息比如“已选3个文件总大小12MB”这个汇总信息可以接Provider里的model数据保持多卡片同步更新。4.3 转换选项卡片格式映射与参数设置转换选项卡片根据文件源卡片选中的文件类型动态生成可转换的目标格式列表。这里有一个业务逻辑需要仔细处理不是所有格式之间都能互相转换。我在转换引擎里维护了一张格式关系表用邻接矩阵的思想记录每种源格式支持哪些目标格式卡片渲染时只读取这张表里的可转换项。拿常见的文档转换举例源格式可转换的目标格式TXTPDF, HTML, DOCXMDHTML, PDF, TXTJSONYAML, XMLCSVXLSX, JSON这个表如果硬编码在后端新增格式时就要发版更新App所以我把它放在了Assets目录下的一个JSON文件里这样后续加转换能力不需要动代码直接更新资源文件就行。卡片上格式选项用Chip组件展示选中的Chip高亮点击切换。参数设置区域也是转换选项卡片的一部分根据不同格式显示不同的参数项。比如PDF转换会有页面大小选项图片转换会有质量滑块文本转换会有编码选择。这些参数我用一个ConversionOption类统一接收转换引擎执行时从options里读取需要的值。滑块这个组件在OpenHarmony上的Flutter适配层面有过一个小问题拖动时数值变化后的重绘跟不上手指速度我的解决办法是把滑块变化事件做防抖处理松手时才真正更新参数值。4.4 任务进度卡片并发转换与实时状态多文件转换时每个文件的进度展示需要一块独立区域。任务进度卡片的设计思路是把每一个转换任务渲染成一个进度行进度行里显示文件名、进度百分比、速度状态以及取消按钮。卡片的顶部还有一个总进度条聚合展示整体完成比例。进度数据的来源是转换引擎执行业务时通过回调上报的。我设计了一个简单的进度回调接口每处理完一个文件块就回调一次进度值进度值计算方式是已处理字节数除以文件总字节数。这个回调频率要控制如果文件块很小回调太频繁会导致UI线程被埋掉。我的做法是每处理完5%才回调一次既保证了进度更新的平滑度又不会影响转换性能。取消操作是进度卡片里容易被忽略但很重要的功能。转换大文件时用户等得不耐烦想取消如果取消机制没有做干净转换引擎还在后台跑界面却显示已取消下次再转换同一个文件就可能会产生脏数据。我在Isolate里通过一个取消标志位来中断任务转换引擎在每个文件块处理完后检查这个标志位如果置位就直接结束转换并清理临时文件。进度卡片里还有一个细节转换完成的时间预估。我用的是移动平均算法记录最近几次文件块的处理耗时预估剩余时间。实测下来虽然不够精准但用户对时间预估的精度容忍度很高有一个大概数字比纯粹转圈体验好很多。4.5 结果预览与分享卡片转换完成之后结果卡片负责展示输出文件的信息并提供几个后续操作预览内容、保存到指定位置、分享到其他应用。预览功能不打算做太重的内置预览器文本类文件直接读取内容显示在卡片里图片类文件通过Flutter的Image组件直接渲染PDF文件则会提示用户使用系统自带的打开方式。这样做的原因是OpenHarmony上的很多文件格式没有现成的Flutter渲染方案与其花大力气做预览不如把文件路径交给系统去打开。分享功能走的是OpenHarmony的分享能力。桥接原生侧后可以调起系统的分享面板让用户选择微信、邮件或其他支持接收该文件类型的应用。这里遇到的一个实际问题是分享大文件时系统面板的响应比较慢原生侧做了一些没必要的预加载最终通过异步调用分享接口解决了。保存位置的选择上也花了一点心思。文件转换出来的结果如果直接保存到私有目录用户要找到它比较费劲。我的做法是在OpenHarmony公共下载目录下新建一个“文件转换助手”的文件夹所有输出文件都存放在这个位置。实测下来用户在文件管理器里找得到后续分享或者上传也方便。5. 卡片组件与平台能力的对接5.1 MethodChannel桥接OpenHarmony原生能力Flutter在OpenHarmony上调用原生能力的方法依然是MethodChannel但通道名、参数类型和错误码体系需要额外注意。在Android上你习惯了随意传Map类型在OpenHarmony的Flutter适配层里有些类型序列化支持还不到位尤其是复杂嵌套的Map或List传着传着就变成空对象了。我的破局思路是所有跨平台数据传输统一用JSON字符串。Dart侧把数据用dart:convert里的jsonEncode序列化成字符串原生侧解析后再做处理返回值也是JSON字符串。虽然看起来绕了一层但兼容性和稳定性明显上升而且调试的时候能直接打日志看JSON内容定位问题快很多。原生侧注册通道的代码如下通过FlutterPlugin的MethodChannel入口绑定处理逻辑class FileBridge : FlutterPlugin, MethodCallHandler { private lateinit var channel: MethodChannel override fun onAttachedToEngine(binding: FlutterPluginBinding) { channel MethodChannel(binding.binaryMessenger, converter/file_channel) channel.setMethodCallHandler(this) } override fun onMethodCall(call: MethodCall, result: Result) { when (call.method) { pickFile - handlePickFile(call, result) saveFile - handleSaveFile(call, result) shareFile - handleShareFile(call, result) else - result.notImplemented() } } }顺带提醒一点OpenHarmony上的原生侧语言如果是ArkTS而不是KotlinMethodChannel的API形式略有差异但设计思路完全一致。重点是处理好异步回调的Result接口不能只回调一次然后就不管了。5.2 文件读取与写入的权限处理文件权限是工具类App最容易翻车的地方。OpenHarmony的权限模型和Android不完全一样有些你熟悉的运行时权限申请代码在这个平台上可能不生效而且错误提示很模糊。我通过实际测试确认读取公共目录文件时需要用ohos.permission.READ_MEDIA或READ_USER_STORAGE这类权限声明。权限申请的入口在哪里写是个讲究。最理想的做法是在需要用到对应权限的卡片首次展示时弹出申请而不是在App启动时一股脑申请全部权限。用户看到权限弹窗和上下文是匹配的通过率会高一些。卡片组件里我会加一个状态位标记权限是否已被授予没有授权就展示一个引导按钮。文件写入权限上我之前遇到的坑是在OpenHarmony某些版本下即使声明了权限写入公共下载目录仍然返回Permission denied。排查下来发现是写入路径中的父目录没有提前创建系统不允许直接创建多级目录。解决方式是在保存文件之前先递归创建所有中间目录权限问题就消失了。这类问题的规律往往是权限声明只是第一步真实文件系统中目录层级和读写权限的协商才是大头。5.3 其他实用平台能力的封装除了文件读写工具卡片组件还用到几个比较冷门但实测有价值的平台能力。剪贴板能力是其中一个。文本转换的场景中用户转换完成之后想把结果直接复制到剪贴板去粘贴到别处。Flutter本身有Clipboard的API在OpenHarmony上也能用但复制的历史记录无法回溯所以我只在结果卡片上提供一个“复制”按钮不做剪贴板历史管理。另一个是系统分享面板。分享能力在上一节提了一下这里再展开说。文件分享到其他应用的核心是文件URI和Intent的组合在OpenHarmony上要额外配置文件类型过滤器。如果只传了URI没传MIME类型分享面板可能会认为这个文件没有应用可接收。我在封装方法里加了一个扩展名到MIME类型的映射函数分享前自动补充实测成功率和Android原生基本一致。最后是弱网状态感知。工具类App看起来和网络无关但部分转换能力之后可能会接云端AI识别OCR我就提前在平台层封装了网络状态监听卡片组件可以根据网络状态提示用户是否开启在线识别功能。这算是一个预留设计目前只接了WiFi和蜂窝网络的判断后续可以直接在这个基础上扩展。6. 实测调试中的踩坑记录与排查链路6.1 模拟器白屏问题从日志到根因的完整排查我实机测试过程中遇到最值得记录的问题是模拟器上白屏。现象是Flutter的启动画面出来之后界面一直渲染不出来日志里有大量渲染管线错误。排查过程是这样的。我先用flutter logs看Dart层有没有异常结果干净得很业务代码还没执行到。接着排除了Dart层的问题开始怀疑引擎初始化异常。日志里出现Unable to create rendering surface这类错误定位到是Flutter引擎在OpenHarmony模拟器上创建的渲染Surface不支持硬件加速。进一步排查发现OpenHarmony模拟器的GPU虚拟化能力和Android模拟器不一样Flutter的Impeller渲染后端在这个环境下无法完成初始化引擎直接选择放弃渲染。解决方式是强制Flutter引擎切到Skia渲染后端在启动参数里加上渲染后端的切换配置白屏问题才消失。这个排查链路的启示是跨平台适配出问题时第一反应不要去改业务代码先确认引擎层是否正常工作。日志里如果出现渲染或Surface相关的错误优先往渲染后端、硬件加速这些方向想能省去很多无谓的调试时间。6.2 卡片动画掉帧Provider重建范围的连锁反应工具卡片组件在展开收起动画时出现过掉帧表现是动画不跟手输入响应明显延迟。起初我以为是动画曲线的问题换了几种曲线效果依旧于是开始检查重建范围。通过Flutter的PerformanceOverlay工具查看重建帧率只有30左右。进一步加了debugPrint在卡片基类的build方法里发现动画过程中整个工具卡片的父容器都在重建不只是发生动画的那一张卡片。定位到原因是Provider订阅粒度不对进度数据的变化触发了包含卡片列表的父级组件重建。解决方案有两个动作。第一步把进度数据拆成独立的ProgressModel只有进度卡片订阅它其他卡片不感知变化。第二部在构建卡片列表时对每张卡片使用RepaintBoundary包一层让卡片自身的绘制不被父级重绘影响。改完之后帧率恢复到55以上基本接近满帧。这里再补充一个经验OpenHarmony低端设备的性能余量没有Android那么充足能减少重建就尽量减少别指望用户拿的都是旗舰硬件。6.3 中文文案与字体在OpenHarmony上的适配细节工具类App用得最多的还是中文文案这就涉及字体渲染的兼容问题。在OpenHarmony上Flutter默认字体的字体族可能是HarmonyOS Sans但这个字体在部分设备上没有完整打包导致中文显示成了系统回退字体观感很怪。我的处理是在应用启动时主动加载一个打包进Assets的中文字体文件并在MaterialApp的theme里设置全局字体族。这样做有两个好处一是绕开了设备字体缺失的问题二是字体风格可控不会因为系统字体版本不一致导致UI风格漂移。另一个和文案相关的坑是文本溢出。卡片宽度在不同设备上差异较大较长的文件名在宽度不够的卡片上会直接撑破边缘或者被省略号截断得很难看。我给文件名区域加了双行限制超长后用渐隐效果代替省略号视觉上更平滑。这套文案适配经验在OpenHarmony这种设备型号庞杂的平台上尤其重要因为没法像Android那样做一套设计稿然后依赖系统自动缩放。字体加载这步还有一个小细节Dart的rootBundle.load读取字体文件时如果文件较大首次加载会很卡。我的做法是把这个字体数据缓存到内存里后续创建TextStyle时直接复用FontLoader已注册的字体名称App冷启动的耗时实测降低了几百毫秒。6.4 Hot Reload在OpenHarmony上的可用性边界开发效率上Hot Reload能不能用直接决定了我写这个项目的耐心。实测下来的结论是可用但边界比Android窄。普通UI调整、状态修改都能正常热更新但新增或删除原生插件、修改MethodChannel通道名这类改动Hot Reload经常失效表现是界面没有更新但也不报错。针对这个问题我的工作流是分层的。纯Dart层的逻辑调整直接用Hot Reload验证涉及原生桥接的代码改动就直接重新flutter run全量安装。虽然全量安装时间长一些但至少不会在一个不确定的状态下浪费时间调一个根本不生效的修改。另外提一句OpenHarmony设备上频繁安装HAP包会发现应用数据没有彻底清除有些旧配置会残留导致新版本表现诡异。遇到这种情况先执行hdc uninstall再重新安装基本都能解决。这个习惯建议从第一天就养成不然问题积累到一定程度很难界定是代码的锅还是环境的锅。我这个项目做下来最大的感受是跨平台框架进入一个新系统的过程中真正的成本不是在Dart代码本身而是在于对系统能力的理解和适配。工具卡片组件这个设计让我在OpenHarmony上快速搭建出了可用的工具App界面也让我对Flutter的渲染和状态管理有了更深入的理解。如果后面社区对OpenHarmony的支持越来越完善这条Flutter路线完全可以作为小团队低成本进入OpenHarmony生态的破局方案。
返回列表