1. 从零搭一个能跑起来的Flutter鸿蒙工程
做OpenHarmony上的Flutter开发,第一个门槛往往不是业务逻辑,而是环境。很多人在Windows上装完Flutter SDK,打开DevEco Studio准备建工程,结果发现两边根本不互通,折腾一天连hello world都跑不到板子上。我这边把环境准备到第一行命令能出画面的完整路径拆开讲,照着做基本不会卡壳。
1.1 工具链选型与版本匹配逻辑
先说结论:Flutter SDK要用OpenHarmony官方维护的分支,不能直接用Google主干版本。OpenHarmony的Flutter适配由OpenHarmony SIG组在维护,仓库地址是gitee.com/openharmony-sig/flutter_flutter,这个分支基于Flutter 3.7.x做了鸿蒙侧的引擎适配和Platform Channel桥接。你要是图省事直接flutter pub get跑主干,编译到ohos target的时候必然报一堆链接错误,因为鸿蒙的渲染层、字体加载、国际化资源路径跟Android根本不是一套。
版本匹配上有个很容易踩的坑:OpenHarmony的设备SDK版本、Flutter SDK版本、DevEco Studio版本三者要能对上。以我用的RK3568开发板为例,系统镜像烧的是OpenHarmony 3.2 Release,对应的SDK是ohos-sdk-windows_linux-public-4.0.0.6,DevEco Studio必须用4.0.0.6及以上(我用的是4.0.0.7)。Flutter分支选OpenHarmony-3.2-Release,这个Tag对应的引擎版本适配了API 9的接口,能直接调鸿蒙的Ability和分布式软总线能力。你要是用Dayu200或者RK3566,镜像和SDK版本同理,但设备树选型上会有差异,后面单独说。
版本对应关系整理成表格,方便排查问题:
| 组件 | 推荐版本 | 注意事项 |
|---|---|---|
| OpenHarmony系统 | 3.2 Release | 对应API 9,4.0.0.6 SDK |
| DevEco Studio | 4.0.0.7 | 必须高于或等于SDK版本 |
| ohos-sdk | 4.0.0.6 | 解压后配置local.properties时需要 |
| Flutter SDK | OpenHarmony-3.2-Release分支 | 不要用Google master |
| Dart SDK | 随Flutter分支自带 | 单独装Dart会版本错乱 |
| 开发板 | RK3568 / RK3566 / Dayu200 | 需要烧录对应系统镜像 |
1.2 环境变量、local.properties和命令行构建
环境变量这块我吃过亏,所以重点说。OpenHarmony版Flutter需要两个关键的环境变量:DEVECO_SDK_HOME指向DevEco Studio内置的SDK目录,一般是D:\DevEcoStudio\sdk;OHOS_SDK_HOME指向解压出来的ohos-sdk目录,解压后你会看到里面还有windows、linux这种平台子目录,配置的时候要指到平台子目录的上一级,否则构建脚本永远找不到toolchains。
命令行的构建流程是:
bash复制flutter create --platforms ohos my_app
cd my_app
flutter pub get
flutter build hap --debug
这里有个很关键的点:--platforms ohos这个参数只存在于OpenHarmony分支的Flutter CLI里,Google主干压根不认识ohos这个platform。另外在pubspec.yaml的同级目录下,需要手动创建local.properties文件,写入SDK路径:
properties复制sdk.dir=/path/to/ohos-sdk/windows
flutter.sdk=/path/to/flutter
如果你不写local.properties,DevEco Studio打开工程时会报"SDK not found",而命令行构建时又会去读环境变量,两边必须保持一致。项目里如果用了其他Native插件,还需要配置ohos.modules和签名信息,但初期只跑Flutter页面不需要签名,flutter build hap --debug默认用debug签名就能装上。
1.3 RK3568设备树选择的困惑与解法
OpenHarmony的RK3568开发板在烧录和内核编译时,设备树(DTS)的选择确实让人头大。网上搜"openharmony的rk3568有许多设备树到底咋选",基本能看到一堆人在问,原因是官方仓库的kernel/linux/linux-5.10里,arch/arm64/boot/dts/rockchip/目录下同时存在rk3568-evb1-ddr4-v10.dtb、rk3568-evb2-linux.dtb、rk3568-toybrick.dtb等十多个文件,每个对应不同的硬件变体。
我的选择逻辑很简单:先看开发板的丝印和原理图标识,再对内存颗粒和网络芯片型号。手上这块板子是EVB1样机,DDR4颗粒,网卡是RTL8211F,对应rk3568-evb1-ddr4-v10.dtb。如果用Dayu200开发套件,通常选rk3568-evb2-linux.dtb。设备树选错最典型的现象是启动日志在[rockchip] probe阶段卡住,或者以太网起不来、HDMI无输出。所以拿到板子第一件事就是确认型号,别看到RK3568就默认同一个dtb。
如果你不确定手里的板子对应哪个dtb,可以看烧录时的config.cfg文件,RK3568的partition配置里通常写了device_tree字段,直接照抄那个名字。烧录系统镜像的时候,parameter.txt里的device_tree分区如果对应不上,板子会反复重启在bootloader阶段,这个属于硬件适配问题,跟Flutter本身无关,但影响开发调试,提前排除能省很多时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 衣橱管家App的需求拆解与页面架构设计
衣橱管家这个项目,核心场景就一个:用户想知道今天穿什么。但背后要处理的东西不少——衣物数据怎么录入、天气数据从哪里来、推荐逻辑怎么算、主界面怎么让用户一眼看懂。我把它拆成四个模块:衣橱数据层、天气接入层、推荐引擎层、UI展示层,每层职责单一,后续要加功能也好扩展。
2.1 用Flutter做鸿蒙端页面,Widget选型和状态管理思路
Flutter在OpenHarmony上跑UI跟在Android上几乎没有差别,因为OpenHarmony的Flutter引擎实现了Android的View映射逻辑,渲染走的是自家Skia引擎,MaterialApp、Scaffold、ListView这些基础Widget直接可用。区别点在于顶部状态栏高度、安全区适配和系统字体,鸿蒙的StatusBar高度不是固定的,需要用MediaQuery.padding.top动态计算。
衣橱管家的主页面我用的是BottomNavigationBar+IndexedStack的结构,三个Tab分别是"今日穿搭"、"我的衣橱"、"天气日历"。这里我特意用IndexedStack而不是PageView+AutomaticKeepAliveClientMixin,原因是衣橱页需要保持滚动位置和筛选状态,IndexedStack默认会保留所有子页面的状态,切Tab不会重建页面,对性能也更友好。
状态管理这块,项目规模不大,我用的是Provider+ChangeNotifier的组合。衣橱数据、天气数据、推荐结果各自是一个Model,统一交给ClosetProvider管理。为什么不直接上Riverpod或者Bloc?因为这类中小型工具App的状态流不算复杂,Provider的写法直观、模板代码少,而且OpenHarmony分支的Dart版本对高版本state库可能会有兼容性问题,越简单的方案越稳。等到功能膨胀到需要异步复杂编排时再重构也来得及。
2.2 天气数据的两种接入姿态:在线接口与模拟降级
天气穿搭App的核心输入是天气数据,但依赖第三方API有个现实问题:免费接口不稳定、失效快。所以我做了"双源设计"——优先走在线接口,请求失败或者没有网络时自动切换到本地模拟数据,并显示"演示数据"标识,避免用户看到空白页不知所措。
在线接口我选的是OpenWeatherMap的/data/2.5/weather接口,按城市名传入参数,返回温度、湿度、风速、天气代号。OpenHarmony上发起HTTP请求有几条路:系统自带的@ohos.net.http、Flutter的dio包、或者http包。但要注意,Flutter的dio在OpenHarmony侧默认用的还是Dart的HttpClient实现,这没问题;但如果你用鸿蒙的@ohos.net.http,要通过MethodChannel桥接,多一层复杂度。所以我直接用dio,少一个桥接层,出问题也好排查。
模拟数据不是随便编几个温度,而是按季节和城市做了四组默认值:北京冬季平均气温-5℃、夏季平均气温28℃、春季15℃、秋季18℃,再叠加一个±3℃的随机波动。这个设计有几个好处:演示环境下不会穿帮;单元测试可以直接用Mock数据跑推荐逻辑,不依赖网络;App上线初期如果后端接口还没就绪,可以先拿模拟数据刷UI,并行推进。
2.3 推荐引擎:温感模型+场景偏好
穿搭推荐不能只看当前气温,否则逻辑太单薄。我建了个轻量级的"温感模型",核心公式是:
体感温度 = 实际温度 - 风速系数 * 风速 - 湿度系数 * (湿度 - 50) / 100
风速系数取0.2,湿度系数取0.4。为什么这么设?因为风速越大,体感越冷;湿度偏高时,夏天会觉得闷热、冬天会觉得阴冷。这两个系数没有行业标准,我是根据气象学里的风寒指数公式简化来的,实测下来在大部分场景下比只看气温要准。
有了体感温度,推荐规则就好写了。温度区间与衣物层级的映射如下:
| 体感温度(℃) | 推荐层级 | 示例组合 |
|---|---|---|
| >=28 | 清凉单穿 | 短袖T恤+薄款牛仔裤/五分裤 |
| 20~27 | 轻便两层 | 长袖衬衫+休闲裤,可加衬衫外套 |
| 12~19 | 三层过渡 | 长袖+针织开衫+厚休闲裤 |
| 5~11 | 保暖四层 | 毛衣+风衣/夹克+长裤 |
| <5 | 严寒叠加 | 羽绒服+厚毛衣+加绒裤+围巾 |
这五档规则写在一个OutfitRecommender类里,输入体感温度、天气描述(晴/阴/雨)、用户设置的"怕冷/怕热"偏移值(默认0,怕冷调+3,怕热调-3),输出一个推荐方案列表。晴雨天还会额外判断:如果天气代号带雨,就追加推荐"防水外套"和"防滑鞋",这部分是用一个简单关键词匹配实现的,没有上NLP模型。
3. 核心功能实现:衣橱数据模型与天气联动
衣橱管家重点肯定是"衣橱"两个字。衣物数据的组织方式,直接决定了推荐结果能不能用。我用的是"分类+标签+属性"三层结构,每组衣物都有类型、材质、颜色、适用温度范围、季节偏好这类字段。录入页做成了表单+预设模板的混合方式,既支持从零手填,也支持从预设模板快速生成。
3.1 衣物模型的字段设计与本地存储方案
ClothingItem这个Model类,字段我定成这样:
dart复制class ClothingItem {
final String id;
final String type; // 衬衫、外套、T恤、牛仔裤...
final String material; // 棉、羊毛、聚酯纤维...
final String color;
final int minTemp; // 适用最低温度
final int maxTemp; // 适用最高温度
final bool isWaterproof; // 是否防水
final bool isWindproof; // 是否防风
final String season; // 春、夏、秋、冬、四季
final String imagePath;
}
minTemp和maxTemp是推荐逻辑的核心:当体感温度落在某个衣物的温度区间内,它才有资格进入候选集。比如一件轻薄羽绒服的温度区间是-5~10,夏天推荐结果里就绝不会出现它。这个设计比单纯按季节标签过滤更细,因为春秋两季温度波动大,按月份判断很容易出错。
本地存储我用的是shared_preferences的setString,把整个衣橱列表序列化成JSON字符串存成一个key。为什么不用数据库?因为衣物数量撑死几十件,每次读写全量JSON不过几KB,数据库反而引入了Sqlite的初始化成本和复杂查询的维护负担。值得一提的是,shared_preferences在OpenHarmony侧的适配是通过MethodChannel映射到鸿蒙的Preferences接口,所以在重启App之后数据依然能持久化。等衣物数量涨到需要按标签组合查询的时候,再升级到drift或者鸿蒙的RelationalStore也不迟。
3.2 拍摄/相册选图与图片存储
衣橱里的每件衣服最好有张图片,推荐穿搭时缩略图一展示,用户一眼就知道是什么。图片来源有两个:拍照和相册。Flutter侧用image_picker插件,OpenHarmony分支对这个插件的支持已经比较成熟,拍照走鸿蒙的CameraKit,相册走PhotoAccessHelper。这里有个细节:鸿蒙3.2的相册权限模型是ohos.permission.READ_IMAGEVIDEO,需要在module.json5里声明,而且运行时要用abilityAccessCtrl申请,不能只在配置文件里写。
图片选完以后,不建议直接存原图路径,因为系统相册里的图片可能会被用户移动或删除,路径会失效。我采用的做法是:把选中的图片拷贝到App私有目录files/wardrobe/下,统一命名为item_<id>.jpg,数据库里只存这个相对路径。这样即使相册原始照片被清理,衣橱展示也不受影响,同时备份和迁移也方便。
实现代码片段(核心逻辑):
dart复制Future<String> saveImageToPrivateDir(String sourcePath, String id) async {
final dir = await getApplicationDocumentsDirectory();
final wardrobeDir = Directory('${dir.path}/wardrobe');
if (!await wardrobeDir.exists()) {
await wardrobeDir.create(recursive: true);
}
final targetPath = '${wardrobeDir.path}/item_$id.jpg';
await File(sourcePath).copy(targetPath);
return targetPath;
}
这里要注意的是异步IO的异常处理,拷贝大图时如果中途出错,可能会残留半截文件,所以我会在copy外面包一层try-catch,失败时删除残留文件并返回空字符串,UI层面看到空路径就提示用户重新选图。
3.3 天气接口接入:URL构造、参数解析与错误兜底
天气接口的具体实现,我直接贴代码:
dart复制class WeatherService {
final String apiKey = 'your_api_key';
final String baseUrl = 'https://api.openweathermap.org/data/2.5/weather';
Future<WeatherModel> fetchWeather(String city) async {
final url = Uri.parse('$baseUrl?q=$city&units=metric&lang=zh_cn&appid=$apiKey');
try {
final resp = await Dio().get(url.toString());
final data = resp.data as Map<String, dynamic>;
return WeatherModel(
temperature: data['main']['temp']!.toDouble(),
humidity: data['main']['humidity']!.toDouble(),
windSpeed: data['wind']['speed']!.toDouble(),
weatherDesc: data['weather'][0]['description']?.toString() ?? '未知',
weatherCode: data['weather'][0]['id']?.toString() ?? '',
);
} catch (e) {
// 网络异常或接口无效时返回模拟数据
return MockWeatherProvider().getDefaultWeather();
}
}
}
解析参数时有个容易踩的坑:main.temp返回的JSON数字,在Dart里反序列化成num类型,直接.toDouble()是安全的;但weather是个数组,取[0]之前要先判断数组不为空,否则极端情况下API返回2开头的错误码时,weather字段可能是空的,直接取下标会抛异常,所以代码里做了data['weather'][0]的安全判断。更稳妥的做法是先整体判断data['cod'] == 200,再取数据。
天气数据的刷新策略我也定了:每次打开今日穿搭页时拉一次,外加下拉刷新手势,不搞定时轮询。因为衣橱App不是天气App,用户对温度的实时性要求没那么高,频繁请求只会增加耗电和流量。实测在DevEco Studio里跑模拟器,网络请求大概300ms能回来,体感上几乎无感。
4. 今日穿搭页与我的衣橱页的UI实现细节
UI这块我花了不少心思在"看一眼就知道今天穿什么"上。今日穿搭页的顶部是一个大幅的温度卡片,显示当前城市、实时温度、天气图标和一句话建议,比如"今天偏凉,建议穿厚外套";下方是推荐穿搭列表,每一件衣物以卡片形式展示图片、名称和温度区间,点击卡片可以查看详情和更换备选。我的衣橱页则是一个瀑布流网格,按分类筛选,支持长按编辑和删除。
4.1 天气卡片的动态主题色
天气卡片的背景色不是写死的,而是根据天气现象自动切换。晴朗用橙色渐变、阴天用灰蓝色渐变、雨天用深蓝色渐变、雪天用白色渐变。颜色渐变用Flutter的Container+BoxDecoration实现,代码如下:
dart复制Widget _buildWeatherCard(WeatherModel weather) {
final gradientColors = _gradientByWeather(weather.weatherCode);
return Container(
height: 180,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: gradientColors,
),
),
child: Stack(
children: [
Positioned(
right: 20,
top: 20,
child: Icon(
_weatherIcon(weather.weatherCode),
size: 64,
color: Colors.white.withOpacity(0.8),
),
),
Positioned(
left: 20,
bottom: 20,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${weather.temperature.toStringAsFixed(0)}°C',
style: TextStyle(fontSize: 46, color: Colors.white, fontWeight: FontWeight.bold)),
Text(weather.weatherDesc, style: TextStyle(fontSize: 16, color: Colors.white)),
],
),
),
],
),
);
}
这里_weatherIcon方法通过weatherCode映射到IconData,OpenWeatherMap的天气代号规则是:2xx雷暴、3xx毛毛雨、5xx雨、6xx雪、7xx雾霾、800晴、80x多云。映射表我写成了静态Map,避免每次构建页面时重复switch判断。
4.2 衣橱网格、筛选与空状态
我的衣橱页用的是GridView.builder,每行两列,卡片等比缩放容易造成宽高比失衡,所以childAspectRatio要根据屏幕宽度动态算。做法是:用LayoutBuilder取到实际宽度,减去左右间距和列间距,除以2得到每列宽度,再除以卡片预期高度得到aspectRatio。这样在不同尺寸的手机上,卡片比例看起来一致。
筛选逻辑我用的是ChoiceChip一行横滚,分类有全部、上衣、下装、外套、鞋袜、配饰。筛选时不是真删列表,而是用FilteredClosetList监听ChangeNotifier,根据选中分类返回过滤后的子列表。衣橱为空时的空状态,我用了一个居中的Column,包含一个衣柜图标和一行提示"衣橱还是空的,点击右下角添加第一件衣服吧",加"立即添加"按钮,避免用户面对空白不知道下一步干什么。
4.3 穿搭推荐列表的卡片动效与交互
推荐卡片不需要太花哨的动效,但要有反馈。每个推荐卡片我加了InkWell水波纹点击效果,点击后弹出一个showModalBottomSheet,展示这件衣物的完整属性和搭配建议,比如"这件牛仔外套的适宜温度是10~20℃,今天体感温度15℃,穿着合适"。如果用户觉得推荐不合理,可以点击"换一件"按钮,推荐引擎会从候选集中跳过当前衣物,重新选一件。
换衣逻辑用了一个简单的随机偏移:候选集排序后,默认取第一件,换衣时从剩余项里随机取一个,但保证新选的衣物温度区间包含当前体感温度。这个交互成本很低,但极大提升了推荐结果的"可解释性",用户能感受到App不是死板地给一个答案,而是可以协商的。
5. 真机调试与打包:DevEco Studio接入Flutter模块
开发完功能不等于交付,最终要能把HAP包装到设备上跑。OpenHarmony上Flutter App的打包路径和Android不尽相同,多了些鸿蒙特有的步骤。这里我把从Flutter工程到DevEco Studio再到RK3568真机的全流程写出来,包括调试时的日志抓取和一些奇奇怪怪的坑。
5.1 Flutter模块嵌入鸿蒙工程的两种方式
我在项目里实际用过的有两条路:一条是全Flutter工程直接构建HAP,适合纯Flutter应用;另一条是原生鸿蒙工程+Flutter Module混合开发,适合已有鸿蒙代码、需要嵌Flutter页面的场景。
方式一:全Flutter工程构建HAP
这个最简单,只需要保证工程根目录的build.gradle里包含com.huawei.ohos:hap插件,并且在entry/build.gradle里配置:
groovy复制dependencies {
implementation project(':flutter')
}
然后执行:
bash复制flutter build hap --debug
产物路径在build/ohos/.../entry-default-signed.hap。这个hap包可以通过hdc install命令装到RK3568上。
方式二:原生工程加Flutter Module
如果你已有鸿蒙工程,想加Flutter页面,需要把Flutter工程作为Module引入。步骤是:
- 在Flutter工程根目录执行
flutter build hap --debug,生成libflutter.so和flutter_assets等产物。 - 在鸿蒙工程的
entry/src/main/resources/rawfile下放置Flutter的flutter_assets目录。 - 在
MainAbility中通过FlutterActivity加载Flutter页面。
这种方式的工作量比方式一多不少,建议优先用方式一,除非鸿蒙原生能力(比如分布式流转)必须和Flutter页面共存在同一个Ability里。
5.2 真机调试的日志与错误定位技巧
OpenHarmony设备的调试日志用hdc命令,类似于Android的adb。抓Flutter侧的日志,常用两类:
bash复制# 查看设备连接状态
hdc list targets
# 实时抓取hilog日志(Flutter的console.log也会输出到这里)
hdc hilog
如果App崩溃,先看崩溃栈里有没有libflutter.so,有的话基本能确定是Dart层异常;如果是Unable to load libflutter_engine.so,那就是Flutter引擎没有正确打包进HAP,检查entry/build.gradle里有没有把libflutter.so打包进native库。
还有一个高频问题:Flutter页面白屏但无crash日志。这多半是引擎初始化阶段就失败了,常见原因是module.json5里缺少ohos.permission.INTERNET权限,导致Dio请求直接抛错,而异常又被吞掉后UI层没拿到数据,卡在白屏。解决办法是看一眼hilog里有没有E/flutter: SocketException,有就先去补权限。
5.3 RK3568板子运行App的功耗与性能观察
搭载RK3568的开发板性能不算强,四核A55跑Flutter的Debug模式会有肉眼可见的卡顿,这是正常的(Debug构建包含Dart VM的JIT和校验逻辑)。想获得接近真实用户的流畅度,要用Release包,命令是:
bash复制flutter build hap --release
Release包跑起来后,页面切换基本能达到60帧,但衣橱网格加载几十张图片时会有掉帧。我做的优化是:缩略图统一压到200x200再显示,用cacheWidth参数让Flutter在光栅化时就缩小图片,减少GPU纹理上载的压力。实测冷启动时间在Release包下约为1.2秒,比Debug包快了一倍不止。如果你的App对启动速度要求高,可以考虑启动时隐藏Flutter首帧,等第一帧绘制完成再展示,这里用FlutterEngine的addFirstFrameCallback可以做到。
6. 常见问题速查表与避坑指南
下面这些坑,基本上是我在开发衣橱管家这个项目时逐个踩过并花了不少时间排查才解决的,整理成速查表给后面做OpenHarmony Flutter开发的朋友一份参考。
| 现象 | 可能原因 | 快速定位方法与解决方案 |
|---|---|---|
flutter build hap报找不到ohos sdk |
未配置DEVECO_SDK_HOME或local.properties |
检查环境变量,确认路径下有toolchains目录 |
| 真机安装HAP失败 | 设备未解锁或hap未签名 | 开发板需要先执行hdc shell param set persist.sys.hilog.debug.on true开启调试;用flutter build hap --debug自带debug签名即可 |
| Flutter页面白屏无响应 | 缺少INTERNET权限或引擎未初始化 | 检查module.json5里是否声明ohos.permission.INTERNET;查看hilog里的E/flutter日志 |
| 页面切换掉帧严重 | 使用Debug包或图片过大 | 改用--release构建;缩略图加cacheWidth限制尺寸 |
| 天气接口在模拟器上一直超时 | 模拟器网络代理问题 | DevEco Studio模拟器默认无法直接访问宿主机网络,配置dio代理指向宿主机IP |
| 相册选择图片后路径无法访问 | 鸿蒙路径权限限制 | 拷贝图片到App私有目录后使用,不要直接引用原始路径 |
| 状态栏遮挡标题 | 未做安全区适配 | 用MediaQuery.of(context).padding.top动态计算顶部间距 |
| 换了设备树后系统起不来 | dtb与板子硬件不匹配 | 对照板子丝印、内存颗粒、网卡芯片确认dtb,重新烧录 |
6.1 关于flutter showlicensepage主题颜色等细节的零散经验
有次在做"关于页面"时,想改Flutter自带的showLicensePage的颜色,发现它默认用的是ThemeData.primaryColor,不是appBarTheme的颜色。这个细节常被忽略:LicensePage内部是自己构建的Scaffold,它不继承AppBarTheme,只遵循ThemeData里的primaryColor。要整体换主题颜色,得在ThemeData(primaryColor: ...)上动手,而不能只改局部。
衣橱管家App整体走的是浅色系,主色用了一个卡其色Color(0xFFB59A75),暖色调符合服装穿搭的调性。建议主题色定义在常量文件里,不要散落在各个页面,不然改起来会怀疑人生。
6.2 热重载在鸿蒙设备上的可用性
OpenHarmony分支的Flutter支持热重载吗?实测下来,r(热重载)和R(热重启)在Debug模式都可以用,但效果没有Android上那么稳定,有时候改了pubspec.yaml里的依赖后热重载不生效,需要完全重启App。更麻烦的是重新build之后,hdc install的效率不高,所以我推荐一套高效迭代方式:日常UI微调用flutter run -d ohos做热重载,涉及原生能力或依赖变更时直接重新构建HAP安装。
这里还要提醒:flutter run -d ohos的时候,终端会一直挂着占用调试端口,调试完要q退出再执行打包,否则下次flutter build hap会报端口占用。
6.3 一些能提升开发效率的小工具组合
开发过程中我习惯用几个组合拳来提升效率:
- Json转Dart Model:用
quicktype根据天气接口的响应JSON直接生成Model类,省去手写fromJson的繁琐。 - 状态管理可视化:用
Provider的Consumer包裹具体组件,调试时在ChangeNotifierProvider里打印变更日志,快速定位是哪个操作触发了UI刷新。 - 真机截图:
hdc shell snapshot_display -f /data/local/tmp/xxx.jpeg,比用手机截图再导出方便得多,尤其是需要记录崩溃画面时。
这些工具虽然不起眼,但组合起来能明显减少重复劳动,让我能把更多精力放在穿搭推荐逻辑本身。我的体会是,OpenHarmony上的Flutter开发跟Android开发在前80%的体验几乎一致,真正花时间的地方在环境配置、权限声明和原生桥接这几个边缘环节,提前把这些摸透,后面写业务代码基本畅通无阻。如果有朋友也想做这个方向,建议先从首页天气卡片和衣橱增删改查两个闭环做起,先把核心链路跑通,再逐步叠加复杂功能。
