说实话第一次在OpenHarmony设备上把Flutter跑起来的时候心情还挺复杂的。折腾了两个晚上才把环境配通中间一度怀疑是不是自己打开方式不对。但当我自己写的“美食烹饪助手”首页卡片真的在鸿蒙平板上渲染出来滑动跟手、动画流畅的时候那种踏实感确实是看一晚上文档都体会不到的。这篇内容就是记录我用 Flutter for OpenHarmony 从零开发一个美食烹饪助手的完整过程重点写大家最关心的“今日推荐”功能是怎么从 0 到 1 落地的。包含环境搭建、状态管理、推荐逻辑、本地缓存、真机调试和各类坑点。适合两类人看一类是已经在 Flutter 上有基础、想试试 OpenHarmony 生态的同学另一类是刚听说 Flutter 能跑在鸿蒙上、想评估这套方案靠不靠谱的人。相信我看完你会少走很多弯路。1. 项目背景与整体设计思路1.1 为什么选 Flutter 而不是 ArkTS 原生先说结论工具型、内容型、UI 密集型的 App在 OpenHarmony 上用 Flutter 是划算的如果是要深度调用系统底层能力、强依赖分布式软总线特性的应用现阶段老老实实用 ArkTS 或者 ArkUI 更省心。美食烹饪助手属于典型的内容工具型 App页面结构固定首页推荐、菜谱详情、我的收藏、卡片式 UI 密集、需要频繁刷新和动画过渡。这类 App 的开发瓶颈从来不在系统 API 的调用深度而在 UI 迭代效率。Flutter 一套代码六个平台跑——Android、iOS、Web、Windows、macOS、Linux现在再加上 OpenHarmony对团队来说意味着鸿蒙生态的适配成本被压缩到一个量级以内。而且说实话OpenHarmony 的北向语言是 ArkTS语法上接近 TypeScript对于纯前端背景的开发者确实友好。但 Flutter 的 Dart 语言 自带渲染引擎这套组合在 UI 一致性和动画表现力上有天然优势。你在 Flutter 里写好了的组件、状态管理、路由方案到了 OpenHarmony 上几乎不用改这种“技能复用”的价值做过跨端迁移的人都懂。1.2 “今日推荐”功能的需求拆解与方案选型“今日推荐”听起来简单拆开看其实有四个核心诉求一是每日更新。用户每天早上打开 App看到的不应该是昨天那一批菜否则就是“假推荐”。推荐结果必须跟日期绑定每天自动轮换。二是有推荐理由。光给一个菜名和图片不够用户需要一个“为什么今天推荐这道菜”的理由比如“时令食材”“热量适中”“适合工作日快手菜”。这是美食类 App 区别于普通菜谱列表的关键体验。三是加载要快。App 冷启动后推荐区如果转菊花转个三秒用户大概率就退出了。首屏必须秒开剩余内容异步补全。四是离线可用。做饭的场景经常在厨房网络信号未必好。推荐结果必须有本地缓存能力没有网也能打开昨天的菜谱。技术选型上状态管理我选了 Provider。原因很直接轻量、官方文档齐全、社区案例多而且对 OpenHarmony 这种生态还不够成熟的平台来说用越主流的方案踩坑后能搜到的解决方案就越多。Riverpod 虽然设计更严谨但引入的概念偏多新手容易绕晕Bloc 在复杂业务场景有优势但对这个小功能属于大材小用。饮食类推荐的核心逻辑是规则引擎 加权打分不是实时流式计算Provider 的 ChangeNotifier 机制完全够用。2. 环境准备与项目初始化2.1 三步搞定 OpenHarmony 开发环境DevEco Studio 与 Flutter SDK 配置OpenHarmony 的开发工具链现在比两年前成熟多了但第一次配置还是有门槛。我直接把验证过的步骤给出来。第一步安装 DevEco Studio 4.0 及以上版本。下载地址在 OpenHarmony 官网选 Windows 或者 macOS 的对应包。安装完成后重点检查一件事——SDK 路径。DevEco Studio 默认会装一套 OpenHarmony SDK通常在你用户目录下的AppData\Local\OpenHarmony\SdkWindows或者~/Library/OpenHarmony/SdkmacOS。这个路径一定要记清楚后面配环境变量要用。第二步获取 Flutter for OpenHarmony 的 SDK。注意这里跟普通 Flutter 不是一回事你需要克隆官方适配分支git clone -b ohos-4.0 https://gitee.com/openharmony/flutter_flutter.git我用的版本是 Flutter 3.7.12 的 ohos 适配分支对应 OpenHarmony API 10。如果你想用更新的版本先去官方仓库看分支列表确认跟手头 DevEco Studio 的 SDK 版本匹配这个匹配关系比你想的重要得多。第三步配置环境变量。需要配置三个# 指向 OpenHarmony SDK 根目录 export DEVECO_SDK_HOME$HOME/ohos-sdk # PATH 加入 flutter 工具 export PATH$PATH:$HOME/flutter_ohos/bin # 开启 ohos 平台支持 export FLUTTER_OHOStrue配置完跑flutter doctor如果看到 OpenHarmony 那一栏是绿色的勾说明环境通了。我当时卡了很久才发现是DEVECO_SDK_HOME指到了 DevEco Studio 的安装目录而不是 SDK 目录环境变量这东西错了不报错只有到构建时才疯给你看。2.2 创建工程并理解 OpenHarmony 平台在 Flutter 中的角色环境通了之后创建工程跟普通 Flutter 项目几乎一样flutter create --platforms ohos cooking_assistant创建完成后你会看到项目里多了一个ohos/目录这就是 OpenHarmony 的工程壳。它内部是标准 OpenHarmony 工程结构包含entry模块、oh-package.json5等。整套东西看着陌生但核心心法很简单Flutter 是泥瓦匠OpenHarmony 是房东。ohos/目录这个“房子”提供房间、水电系统能力、窗口、事件投递你的 Flutter 代码负责把房间装修漂亮。在 OpenHarmony 上跑 Flutter 应用本质上是通过 Flutter 引擎的 OpenHarmony 适配层把 Dart 代码渲染成图像输出到系统窗口。此时我建议你在pubspec.yaml里先把依赖加好dependencies: flutter: sdk: flutter provider: ^6.1.1 shared_preferences: ^2.2.2 intl: ^0.18.1intl是为了处理日期格式化和国际化后面写推荐逻辑时能用到。shared_preferences做缓存轻量够用。为什么不引入网络请求库因为第一阶段我准备用本地 JSON 模拟数据源先把功能跑通。等后续接真实接口时再加 dio 也不迟。注意pub 包版本要选择支持 ohos 平台的。绝大多数纯 Dart 包在 OpenHarmony 上没问题但涉及原生插件比如 shared_preferences则要求插件实现 ohos 端。好在 OpenHarmony 社区已经把常用插件的适配做了大部分shared_preferences、path_provider 这类高频包都有 ohos 实现直接在 pub.dev 搜shared_preferences_ohos就能找到。如果某个包没有 ohos 实现项目启动时会直接报 MissingPluginException。3. “今日推荐”核心功能实现3.1 数据模型与本地菜谱库设计写任何功能的第一步不是 UI 不是状态而是数据模型。我把菜谱模型设计成下面这样class Recipe { final int id; final String name; final String description; final ListString ingredients; final int duration; // 烹饪时长单位分钟 final String difficulty; // 简单 / 中等 / 困难 final int calories; // 千卡 final String imagePath; // 本地图片资源路径 final ListString tags; // 标签如 [时令蔬菜, 快手菜, 高蛋白] final double rating; // 用户评分用于加权 Recipe({ required this.id, required this.name, required this.description, required this.ingredients, required this.duration, required this.difficulty, required this.calories, required this.imagePath, required this.tags, required this.rating, }); factory Recipe.fromJson(MapString, dynamic json) { return Recipe( id: json[id], name: json[name], description: json[description], ingredients: ListString.from(json[ingredients]), duration: json[duration], difficulty: json[difficulty], calories: json[calories], imagePath: json[imagePath], tags: ListString.from(json[tags]), rating: (json[rating] as num).toDouble(), ); } }我把菜谱数据放在assets/data/recipes.json里管这个叫“本地菜谱库”。一开始放了 20 道菜番茄炒蛋、红烧排骨、清蒸鲈鱼、宫保鸡丁、上汤娃娃菜……每道菜都把食材、热量、时长、标签写清楚。为什么用 JSON 而不是 Dart 对象直接初始化好处是可扩展——以后接远程更新服务端下发同样的 JSON 结构前端解析逻辑不需要动一行代码。这就是前后端分离思维在客户端数据层的体现。3.2 推荐引擎日期种子 加权评分的规则实现“今日推荐”最核心的逻辑就是推荐引擎。我没有上机器学习大材小用而是用了“日期种子随机 多因子加权评分”的规则方案。核心思路每天的推荐列表要稳定且不同。稳定指的是同一天内无论用户刷新多少次推荐结果都是一样的——不能每次刷新都变否则用户会觉得系统有问题。不同指的是换一天必须整体换掉。用日期做随机种子天然满足这两个要求class RecommendationEngine { static const ListRecipe _allRecipes [...]; // 菜谱库 static ListRecipe generate(DateTime date, {int count 6}) { // 以日期为种子保证每天的推荐序列稳定 final seed date.year * 10000 date.month * 100 date.day; final random Random(seed); final scored _allRecipes.map((recipe) { var score recipe.rating * 10 random.nextDouble() * 10; // 季节加权根据月份匹配时令食材标签 if (_isSeasonal(recipe, date.month)) { score 15; } // 工作日优先推荐快手菜 if (_isWeekday(date) recipe.duration 30) { score 10; } // 周末推荐耗时菜 if (_isWeekend(date) recipe.duration 45) { score 8; } return _ScoredRecipe(recipe, score); }).toList(); scored.sort((a, b) b.score.compareTo(a.score)); return scored.take(count).map((e) e.recipe).toList(); } static bool _isSeasonal(Recipe recipe, int month) { if (month 3 month 5) return recipe.tags.contains(春季食材); if (month 6 month 8) return recipe.tags.contains(夏季开胃); if (month 9 month 11) return recipe.tags.contains(秋季润燥); return recipe.tags.contains(冬季暖胃); } }这里有个细节值得说为什么评分公式是评分 * 10 随机数 * 10因为菜谱库里的 rating 范围在 3.5 到 5.0 之间乘以 10 后是 35 到 50 分随机项 0 到 10 分季节加权 15 分。这个权重关系是经过设计的——季节加权和场景加权必须能影响最终排序但不能完全淹没基础评分。如果随机项是 0 到 100那每天推荐出来的菜就是纯随机用户评分失去意义如果只有基础评分那每天都推荐同几道高分菜失去“每日新鲜感”。推荐引擎输出的是第一道菜的“主推菜”和后五道的“副推列表”。主推菜会展示大图和推荐理由副推列表则是紧凑卡片。3.3 Provider 状态管理与组件通信实战推荐引擎写好了接下来是怎么把数据推送到 UI。先说组件通信。Flutter 的组件通信分几种父子传参构造参数传递、子父回调回调函数、跨层级共享InheritedWidget/Provider。在今日推荐场景问题是这样的HomePage里嵌着TodayRecommendationSection里面又嵌着RecommendationCard而数据是异步加载的。如果每个层级都用构造函数传参页面会变成一座传参屎山——改一个字段整条链路上的构造函数全得改。Provider 方案就是为解决这个问题存在的。在main.dart顶部注入void main() { runApp( ChangeNotifierProvider( create: (_) RecommendationProvider()..loadToday(), child: const CookingApp(), ), ); }RecommedationProvider 继承 ChangeNotifierclass RecommendationProvider extends ChangeNotifier { ListRecipe _todayRecipes []; bool _loading true; bool _loadedFromCache false; ListRecipe get todayRecipes _todayRecipes; Recipe? get featured _todayRecipes.isEmpty ? null : _todayRecipes.first; bool get loading _loading; Futurevoid loadToday() async { final now DateTime.now(); // 先读缓存让首屏秒开 final cached await CacheHelper.read(recommend_${now.year}_${now.month}_${now.day}); if (cached ! null) { _todayRecipes cached; _loadedFromCache true; _loading false; notifyListeners(); } // 再走引擎生成最新推荐后台异步比较 final generated RecommendationEngine.generate(now); if (!_loadedFromCache || generated.isNotEmpty) { _todayRecipes generated; _loading false; notifyListeners(); // 写缓存下一次冷启动直接用 await CacheHelper.write(recommend_${now.year}_${now.month}_${now.day}, generated); } } Futurevoid refresh() async { _loading true; notifyListeners(); await Future.delayed(const Duration(milliseconds: 600)); _todayRecipes RecommendationEngine.generate(DateTime.now()); _loading false; notifyListeners(); } }子组件读状态的姿势很关键。用context.watchT()会把组件注册为 Provider 的监听者Provider 一旦notifyListeners()组件自动重建。用context.readT()则是一次性读取不监听。推荐列表卡片用 watch按钮点击时读数据用 read——这一点搞反了会出现局部刷新乱套或者性能浪费。实际写页面时我是这样在一个组件里同时用到两种读取方式的class TodayRecommendationSection extends StatelessWidget { override Widget build(BuildContext context) { final provider context.watchRecommendationProvider(); if (provider.loading provider.todayRecipes.isEmpty) { return const Center(child: CircularProgressIndicator()); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _FeaturedCard(recipe: provider.featured!), const SizedBox(height: 12), _RecommendationListView(recipes: provider.todayRecipes.skip(1).toList()), ], ); } }刷新按钮的做法是点击时不需要等 UI 跟随状态变化直接触发操作即可。IconButton( icon: const Icon(Icons.refresh), onPressed: () context.readRecommendationProvider().refresh(), )3.4 推荐卡片 UI 实现从布局到动效细节推荐卡片的 UI 我花了不少心思。主推卡用的是一个 16:9 的大图卡片左上角贴“主厨推荐”的标签底部是菜名、推荐理由和关键参数行。副推列表是一行横向滚动的紧凑卡片。先看主推卡的代码class _FeaturedCard extends StatelessWidget { final Recipe recipe; const _FeaturedCard({required this.recipe}); override Widget build(BuildContext context) { return Container( height: 260, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), image: DecorationImage( image: AssetImage(recipe.imagePath), fit: BoxFit.cover, ), ), child: Stack( children: [ // 底部渐变遮罩保证文字可读性 Positioned.fill( child: DecoratedBox( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, Colors.black.withOpacity(0.65), ], ), ), ), ), Positioned( left: 16, bottom: 16, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( recipe.name, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 6), Text( _buildReason(recipe), style: const TextStyle(color: Colors.white70, fontSize: 14), ), const SizedBox(height: 8), Row( children: [ _InfoChip(icon: Icons.timer_outlined, text: ${recipe.duration} 分钟), const SizedBox(width: 8), _InfoChip(icon: Icons.local_fire_department_outlined, text: ${recipe.calories} 千卡), const SizedBox(width: 8), _InfoChip(icon: Icons.restaurant_outlined, text: recipe.difficulty), ], ), ], ), ), ], ), ); } }卡片切换的动效用的是 Flutter 自带的方向性滑动给推荐区包一个AnimatedSwitcher每次推荐结果更新时新卡片从右侧滑入。这个动效应验了 Flutter 的两大卖点之一UI 表现力的跨平台一致性。在 OpenHarmony 上跑这个动画跟 Android 上的表现几乎没有差别因为动画计算和渲染都在 Flutter 引擎层完成跟系统 UI 框架没有半毛钱关系。4. 数据持久化与加载优化4.1 本地缓存策略让“今日推荐”离线也能用美食烹饪助手的使用场景决定了它必须能在弱网甚至无网环境下工作——厨房信号差是客观现实。所以我给推荐结果做了本地缓存。实现方式用的是 shared_preferenceskey 的设计是关键Futurevoid _writeTodayCache(ListRecipe recipes) async { final prefs await SharedPreferences.getInstance(); final dateKey DateFormat(yyyyMMdd).format(DateTime.now()); prefs.setString(recommend_$dateKey, jsonEncode(recipes)); }仔细看这个 key——我把日期拼进去了。好处是每天的推荐天然是不同 key缓存不会互相覆盖坏处是如果用户一个月不打开 App本地会堆积三十个 key 的垃圾数据。所以顺手做了一层清理读取时检查历史 key 里超过一周的直接删掉。static Futurevoid _cleanupOldCache() async { final prefs await SharedPreferences.getInstance(); final keys prefs.getKeys().where((k) k.startsWith(recommend_)); final today DateTime.now(); for (final key in keys) { final datePart key.split(_).last; final cachedDate DateTime.parse(datePart); if (today.difference(cachedDate).inDays 7) { prefs.remove(key); } } }你可能要问缓存的数据和当天生成的推荐数据冲突怎么办我的策略是缓存优先。loadToday()先读缓存如果命中就直接展示不重新生成了。这样有两个好处一是冷启动速度极快读个字符串比跑推荐引擎快一个数量级二是同一天的展示结果绝对一致不会出现用户早上看完中午刷新变成另一批菜的情况。只有缓存没有命中时才走引擎生成。4.2 图片资源管理与对 App 包体积的克制菜谱卡片要好看图片是核心但图片也是 OpenHarmony 应用包体积的大头。20 道菜的图片如果都用高清 JPG 塞进assets/每张 500KB 算少的一个长图轮播页面做下来保底 10MB。对于工具型 App 来说这个体积在鸿蒙生态里算是个负担。我的做法是分场景处理主推大图的精度要求高用 1080p 的 JPG副推小卡片是 300px 见方的缩略图用 WebP 格式单张控制在 20KB 以内。另外在pubspec.yaml里对 assets 做了路径分包assets: - assets/data/recipes.json - assets/images/hero/ # 主推大图 - assets/images/thumb/ # 副推缩略图这样资源能按需加载目录结构也清晰。等以后接入后端接口把图片 URL 化就不用管体积问题了——但离线优先的策略建议保留缓存图片到本地也走shared_preferences存 URL 列表配合文件系统缓存那都是后话。5. 常见问题与排查实录5.1 Flutter 新建项目后跑不起来的三个高频原因这个问题在热搜词里出现频率高到离谱我猜很多人第一步就挂在环境上。我总结遇到的三个高频原因第一个是OpenHarmony SDK 版本和 Flutter 适配分支不匹配。DevEco Studio 4.0 对应 API 10配 3.7.12 的 ohos 分支没问题但如果你的 DevEco Studio 升级到 4.1API 11还拿老的 ohos 分支跑构建产物会出现链接错误。解法去 flutter_flutter 仓库的ohos分支列表里找你 SDK 版本对应的分支别贪新。第二个是Gradle 下载超时导致构建失败。OpenHarmony 工程构建也是走 Gradle 供应链在国内环境下首次构建会有大量的下载依赖网络稍差就挂。解法在ohos/目录下的build-profile.json5里配置镜像源或者用 DevEco Studio 内置的 SDK 管理器先把依赖预下载完再跑 flutter。第三个是环境变量问题导致设备识别不了。症状是 flutter run 说找不到设备但 DevEco Studio 明明能连上。检查FLUTTER_OHOStrue是否设置了以及 hdcOpenHarmony 的设备连接工具类似 Android 的 adb是否加入了 PATH。排查命令hdc list targets无输出就说明 hdc 没找到设备优先检查 USB 调试模式和驱动如果输出乱码检查 hdc 版本是否跟设备系统版本匹配。OpenHarmony 的 hdc 版本跟设备版本不匹配时会出现“能发现设备但无法连接”的诡异状态这时候手动更新本机 hdc 到对应版本即可。5.2 状态管理在 OpenHarmony 上的坑与对应解法我以为 Provider 这套机制在 OpenHarmony 上是天然跑通的毕竟它只是 Dart 层的代码逻辑跟系统无关。但实际开发中踩了两个不大不小的坑。第一个坑状态更新了UI 不刷新。排查了半天最后发现是构造 Provider 时create里用了异步方法但没有正确处理。在create: (_) RecommendationProvider()..loadToday()这段代码里loadToday()是异步的而create执行完会立即调用 Provider 的首次读取。如果首帧 UI 在异步方法完成前就被 build读到的就是空列表。我的处理方式是给 Provider 内部加了 loading 状态UI 在_loading _todayRecipes.isEmpty时显示加载圈等异步通知后重建问题就解决了。看起来简单但这其实是个 Provider 新手十个人八个会遇到的细节。第二个坑某些子组件不在 Provider 作用域内导致context.watch直接抛 ProviderNotFoundException。这个多数是因为根 widget 是MaterialApp而不是ChangeNotifierProvider直接包在页面外层。我的建议是 Provider 尽量放在runApp的最外层包住整个 App 而非某个页面这样任何一级组件都能安全读取。第三个坑和 Impeller 有关。Flutter 3.7 在 Android 上默认开启 Impeller 渲染引擎但 ohos 分支的 Impeller 适配还不完善跑起来会有偶发性的渲染撕裂。我的解法是显式关闭 Impeller切回 Skiaflutter run --no-enable-impeller或者在main.dart里写死void main() { FlutterRendering.impellerEnabled false; runApp(...); }等 ohos 分支后续版本把 Impeller 适配补齐了再打开不迟。5.3 真机调试技巧和常见报错速查表在 OpenHarmony 真机上调试体验跟 Android 的 adb 逻辑类似但不完全一样。我常用的一套组合# 查看设备列表 hdc list targets # 安装产物到设备 hdc install entry/build/default/outputs/default/entry-default-signed.hap # 看设备日志按关键字过滤 hdc shell hilog | grep -i flutter # 抓取崩溃栈 hdc shell hilog -b D | grep FATAL\|Exceptionhilog 是 OpenHarmony 的日志系统平时 Flutter 的 debugPrint 输出也会走这里。如果你在设备上运行 App刷日志主要靠 hilog而不是 flutter logs。下面这张速查表是我这次开发过程中整理的高频问题清单直接抄作业现象可能原因排查与解法flutter run 找不到 ohos 设备FLUTTER_OHOS 未设置 / hdc 不在 PATH设置环境变量确认hdc list targets有输出构建时 GN 配置报错Flutter 适配分支与 SDK 版本不匹配检查 flutter --version切换对应 ohos 分支冷启动后推荐卡片空白Provider 异步加载时序问题给 Provider 加 loading 状态UI 判断空列表时显示加载圈真机跑起来图片加载失败assets 未正确声明检查 pubspec.yaml 的 assets 路径目录不能有变量或通配符推荐列表每次刷新都不一样随机种子不是日期确认用DateTime.now()转年月日后创建 Random不能直接用 now 本身状态刷新但 UI 不动context.watch / context.read 混用需要监听重建的地方用 watch只在事件回调里触发读数据页面切换卡顿图片分辨率过高真机解码耗时副推卡片用 WebP 缩略图主推大图控制在 1080p 以内hilog 刷不到 flutter 输出hilog 缓存过大 / 过滤不严用-b D调大缓冲区按 tag 过滤后再看这里面最值得强调的就是“推荐列表每次刷新都不一样”的坑。我一开始在RecommendationEngine.generate里写的是Random(DateTime.now().millisecondsSinceEpoch)结果每次进入页面都会生成新序列用户下拉一次换一批菜测试反馈说“系统是疯了吧”。改成以年月日组成整数做种子后同一天内任何时候进入结果都稳定一致。这个细节做推荐类功能的人早晚会遇到。5.4 集成模式的补充把 Flutter 模块塞进已有 OpenHarmony 工程最后补充一个很多同学问过的问题不是从零创建 Flutter 工程而是把 Flutter 模块集成到已有 OpenHarmony 工程里。这种场景在 OpenHarmony 上目前还不像 Android 的 Flutter AAR 方案那么顺手但路子是通的。核心思路是在已有 ohos 工程的ohos/模块里引用 Flutter 的 HAR 包然后在 ArkTS 侧用 Flutter 提供的容器组件挂载 Flutter 页面。具体做法是找 flutter 构建产物里的flutter.har塞进ohos/entry/libs/下接着在entry/src/main/ets/pages/Index.ets里声明容器节点。步骤不复杂但版本约束很敏感Flutter HAR 版本和 OpenHarmony SDK 版本必须严格对应。如果你是单模块 App 从零起步建议直接走flutter create路线如果团队已经有完整的 OpenHarmony 应用只是想渐进接入某几个 Flutter 页面再考虑集成模式。步子别迈太大Flutter 模块集成到原生工程的调试链路比纯 Flutter 工程复杂不少没有充分理由我不推荐一上来就这么干。结尾开发这个美食烹饪助手的“今日推荐”功能前后花了约一星期。真要说最深的体会不是 Flutter 在 OpenHarmony 上跑得有多顺畅而是跨端开发的边界感Flutter 负责 UI 和业务逻辑这一层在鸿蒙生态里是真的能打但涉及系统能力对接、平台适配这些地方必须清楚哪些是成熟的、哪些还在快速迭代。最后分享一个我自己的小习惯在 OpenHarmony 上调试 Flutter 应用每次改动代码后不要急着上真机先flutter analyze跑一遍静态检查再flutter build hap --debug确认构建产物能出。高频的小步验证看起来多花几分钟实际是省时间最快的办法——总好过改了十行代码直接上真机然后对着 hilog 里几百行日志大海捞针。下一步我准备把这个项目的推荐逻辑从纯本地规则升级成接口拉取模式接入真实的食材库和用户收藏数据做个性化排序到时候再写一篇跟你们分享。
阅读完成 · 觉得有帮助?