ARTICLE DETAIL

资讯详情

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

HarmonyOS NEXT 迁移实战:Flutter 食谱 App 源码构建与避坑指南

HarmonyOS NEXT 迁移实战:Flutter 食谱 App 源码构建与避坑指南 简介这份源码面向希望上手 HarmonyOS NEXT 与 Flutter 跨平台开发的移动应用开发者以一款食谱 App 为载体演示如何将 Flutter 的跨平台 UI 能力与 HarmonyOS NEXT 的分布式特性结合解决多设备、多终端下食谱查询与浏览体验不一致的问题。资源包共 36 个文件约 113KB以 11 个 json5 与 8 个 json 配置依赖和构建信息7 个 ets 承载鸿蒙事件逻辑2 个 ts 提供类型化代码另有 4 个 png 界面素材、2 个 txt 说明及 gitignore 等辅助文件目录涵盖 AppScope、entry、mock、ohosTest 等模块结构清晰便于按层阅读。目前已有 307 人学习下载。读者可从中获取一套可运行的迁移示例理解 json5 配置的灵活扩展、TypeScript 静态检查带来的可维护性以及响应式界面与设备协作的落地思路适合作为跨平台应用开发的学习范本与二次开发起点。1. 从 Flutter 到 HarmonyOS NEXT这套食谱 App 源码到底能不能跑去年帮一个做智能厨电的朋友看项目他们想把现有的 Flutter 食谱应用迁到 HarmonyOS NEXT 上团队折腾了两周没跑通最后发现卡在 hvigor 的构建配置和 oh-package 依赖解析上。这套「基于 HarmonyOS-NEXT 与 Flutter 的迁移食谱 App 设计源码」正好是同类场景的完整工程35 个文件里 11 个 json5、8 个 json、7 个 ets、2 个 ts结构上是一个标准的 Stage 模型工程不是那种只丢几个页面文件的半成品。它解决的核心问题是让你看到一个 Flutter 风格的食谱应用在 HarmonyOS NEXT 工程里怎么组织入口、怎么配依赖、怎么把资源挂进 AppScope。适合两类人——正在做 HarmonyOS NEXT 应用迁移的 Flutter 开发者以及想拿一个真实工程练手 hvigor 构建链的鸿蒙新手。源码本身不包含 Flutter 引擎的完整嵌入方案但工程骨架和配置链路是齐的照着改能省掉大量试错。2. 工程结构拆解35 个文件里哪些是骨架哪些是肉2.1 顶层目录与模块划分拿到压缩包解压后第一眼看到的是 AppScope、entry、hvigor 三个顶层目录加上一堆散落的 json5 和 ts 文件。这个布局是 HarmonyOS NEXT Stage 模型的标准形态和传统 FA 模型完全不同。AppScope 放的是应用级配置和全局资源entry 是主 HAP 模块hvigor 是构建工具链的配置目录。具体到文件层面几个关键角色必须分清文件/目录作用能不能动AppScope/app.json5应用包名、版本、图标、标签包名和版本必须改AppScope/resources全局资源图标和字符串可替换entry/src/main主模块源码和资源核心开发区entry/src/main/module.json5模块配置入口 ability按需改hvigor/hvigor-config.json5构建工具版本和插件版本要对齐oh-package.json5项目级依赖声明按需增删build-profile.json5构建产物和签名配置签名必须改entry 下面的 src 又分了 main、mock、test、ohosTest 四个子目录。main 是真正的业务代码mock 放的是测试桩数据test 和 ohosTest 分别是本地单元测试和设备测试。很多新手拿到工程直接改 main 里的东西跑不起来就慌其实先看 mock 里的数据结构能帮你快速理解这个食谱 App 的数据模型长什么样。2.2 json5 与 json 的分工逻辑这个工程里 json5 文件有 11 个json 文件有 8 个数量上 json5 占了大头。为什么不用纯 json因为 json5 支持注释、尾逗号、单引号写配置的时候不用那么憋屈。HarmonyOS NEXT 的构建链从 API 12 开始原生支持 json5 解析所以 app.json5、module.json5、build-profile.json5 这些核心配置全用 json5 写。但注意不是所有配置都能用 json5。oh-package-lock.json5 虽然带 json5 后缀但它的内容格式是锁文件手改容易出问题。oh-package.json5 才是你声明依赖的地方类似 npm 的 package.json。这两个文件的关系是你改 oh-package.json5执行构建时工具会自动更新 oh-package-lock.json5。如果你手动改了锁文件下次构建可能被覆盖或者直接报依赖解析失败。// oh-package.json5 典型结构 { name: recipe_app, version: 1.0.0, description: 食谱应用, main: , author: , license: Apache-2.0, dependencies: { // 这里放三方库依赖 }, devDependencies: { // 这里放构建期依赖 } }上面这段是 oh-package.json5 的骨架。dependencies 里放运行时需要的包devDependencies 放只在构建和测试时用的包。这个工程本身没有引入额外的三方库所以这两个字段大概率是空的或者只有基础依赖。你如果要加网络请求库或者状态管理库就往 dependencies 里塞然后执行ohpm install让工具去拉包并更新锁文件。2.3 ets 与 ts 的边界7 个 ets 文件和 2 个 ts 文件这个比例说明主体代码用 ArkTS 写也就是 .ets 后缀。ArkTS 是 HarmonyOS NEXT 的应用开发语言基于 TypeScript 扩展了声明式 UI 和状态管理。那 2 个 ts 文件干什么用通常是放纯逻辑工具函数或者类型定义不涉及 UI 构建的部分可以写成 ts让编译器按标准 TypeScript 处理。// 典型的 ts 工具文件recipe_utils.ts export interface RecipeItem { id: number; name: string; ingredients: string[]; steps: string[]; } // 按关键词过滤食谱 export function filterRecipes(list: RecipeItem[], keyword: string): RecipeItem[] { if (!keyword) return list; const lower keyword.toLowerCase(); return list.filter(item item.name.toLowerCase().includes(lower) || item.ingredients.some(i i.toLowerCase().includes(lower)) ); }这个工具文件定义了一个 RecipeItem 接口和一个过滤函数。接口描述食谱的数据结构过滤函数接收列表和关键词返回匹配的项。逻辑很直白但关键点是这个 ts 文件被 ets 文件 import 时类型信息会保留ArkTS 编译器能识别。如果你把这段逻辑直接写在 ets 里也能跑但拆出来更干净测试也好写。ets 文件里则是 Entry、Component、State 这些 ArkTS 装饰器的天下。入口页面通常有一个 Entry 标记的组件里面用 build 方法描述 UI 树。食谱列表页会用 List 组件配合 ForEach 渲染详情页用 Navigation 或者 router 跳转。这些和 Flutter 的 Widget 树思路相通但语法完全是两套。3. 环境搭建与首次构建从零到跑通的血泪步骤3.1 DevEco Studio 版本与 SDK 对齐跑这个工程的第一步不是打开代码而是确认你的 DevEco Studio 版本和 SDK 版本。HarmonyOS NEXT 的 API 版本迭代很快API 12 和 API 11 的构建配置有差异。这个工程的 hvigor-config.json5 里会写明 hvigorVersion 和依赖的 plugin 版本你打开这个文件看一眼然后去 DevEco Studio 的 SDK Manager 里确认对应版本的 SDK 已经下载。常见做法是DevEco Studio 用 5.0 以上版本SDK 选 API 12 或更高。如果你本地只有 API 11 的 SDK构建时会报hvigor plugin not found或者compatibleSdkVersion mismatch。这时候要么升级 SDK要么改 build-profile.json5 里的 compatibleSdkVersion 字段往下调但往下调可能遇到 API 不兼容的问题不推荐。# 检查本地已安装的 SDK 版本命令行方式 # 在 DevEco Studio 的 terminal 里执行 ohpm -v # 输出示例ohpm 5.0.0 # 再检查 hvigor 版本 hvigorw -v # 输出示例hvigor 5.0.0这两个命令分别查 ohpm 包管理器和 hvigor 构建工具的版本。版本号要和 hvigor-config.json5 里声明的一致不一致就改配置文件或者升级工具。我一般会先把 hvigor-config.json5 里的版本号抄下来然后逐个核对本地工具版本省得构建到一半报错再回头查。3.2 签名配置与 build-profile.json5 修改HarmonyOS NEXT 的应用安装必须签名没签名的 HAP 装不进设备。build-profile.json5 里有一个 signingConfigs 字段默认可能是空的或者指向一个不存在的证书。你需要用 DevEco Studio 的自动签名功能生成调试证书或者手动配置。自动签名的操作路径是File → Project Structure → Signing Configs → 勾选 Automatically generate signature。IDE 会帮你生成证书和 profile 文件并自动填进 build-profile.json5。手动配置的话需要先在 AppGallery Connect 里创建应用、下载证书和 profile然后在 build-profile.json5 里填路径。// build-profile.json5 签名相关片段 { app: { signingConfigs: [ { name: default, type: HarmonyOS, material: { certpath: C:/Users/xxx/.ohos/config/default_xxx.cer, storePassword: xxxxx, keyAlias: debugKey, keyPassword: xxxxx, profile: C:/Users/xxx/.ohos/config/default_xxx.p7b, signAlg: SHA256withECDSA, storeFile: C:/Users/xxx/.ohos/config/default_xxx.p12 } } ] } }certpath 是证书文件路径profile 是描述文件路径storeFile 是密钥库文件。这三个文件缺一不可。storePassword 和 keyPassword 是密钥库和密钥的密码自动签名生成的密码 IDE 会帮你填。如果你手动改过密码这里要同步改。signAlg 是签名算法一般用 SHA256withECDSA别乱改。3.3 依赖安装与首次构建命令签名配好之后在项目根目录执行依赖安装。HarmonyOS NEXT 用 ohpm 而不是 npm命令是ohpm install。这个命令会读 oh-package.json5把依赖拉到本地 oh_modules 目录同时生成或更新 oh-package-lock.json5。# 在项目根目录执行 ohpm install # 安装完成后执行构建 hvigorw assembleHap --mode module -p productdefault # 如果想清理构建缓存后重新构建 hvigorw clean hvigorw assembleHapohpm install的输出会告诉你装了多少个包有没有报错。如果卡在某个包下载不动检查网络或者换 ohpm 的 registry。hvigorw assembleHap是构建 HAP 包的命令--mode module表示按模块构建-p productdefault指定构建产物类型。构建成功后HAP 文件会生成在 entry/build/default/outputs/default/ 目录下。构建过程中最常见的报错是Failed to resolve ohpm dependencies这通常是 oh-package.json5 里写了不存在的包或者版本号格式不对。另一个高频报错是hvigorfile.ts execution failed这多半是 hvigorfile.ts 里的构建脚本逻辑有问题比如引用了不存在的插件。遇到这两个报错先看完整日志的最后 20 行定位到具体文件和行号再改。4. 避坑与排查迁移食谱 App 时最容易翻车的五个点4.1 坑一module.json5 里 entry ability 的 srcEntry 路径写错现象构建成功但安装到设备后打开闪退日志里报Ability not found或者srcEntry path invalid。原因module.json5 里 entry 模块的 abilities 数组中srcEntry 字段指向的 ets 文件路径不对。这个路径是相对于 entry/src/main/ets 目录的不是相对于项目根目录。很多人从 Flutter 转过来习惯写绝对路径或者从 src 开始写结果路径解析失败。解决打开 entry/src/main/module.json5找到 abilities 数组确认 srcEntry 的值形如srcEntry: ./ets/entryability/EntryAbility.ets。注意开头的./和中间的ets/层级。如果你把 EntryAbility.ets 移到了别的目录这里要同步改。改完重新构建安装。4.2 坑二AppScope/app.json5 的 bundleName 与签名不匹配现象构建通过签名也配了但安装时报signature verification failed或者bundleName mismatch。原因app.json5 里的 bundleName 必须和签名 profile 文件里绑定的 bundleName 完全一致。自动签名时 IDE 会根据 app.json5 里的 bundleName 去生成 profile但如果你后来手动改了 bundleNameprofile 没重新生成就会不匹配。解决先确认 app.json5 里的 bundleName 是什么然后去 AppGallery Connect 或者本地 profile 文件里核对。如果不一致要么改回原来的 bundleName要么重新生成签名。自动签名的场景下删掉 .ohos/config 目录下的旧证书重新走一遍自动签名流程。4.3 坑三json5 文件里用了 json 不支持的语法但工具版本太低现象构建时报Unexpected token或者Invalid json5 format指向某个 json5 文件的某一行。原因json5 支持注释和尾逗号但低版本的 hvigor 或者 ohpm 可能只按严格 json 解析。如果你的工具版本低于 API 12 对应的版本json5 里的注释会被当成非法字符。解决先确认 hvigor-config.json5 里声明的 hvigorVersion 是否支持 json5。如果不支持要么升级工具版本要么把 json5 文件里的注释和尾逗号去掉退化成严格 json。我一般会优先升级工具因为 json5 的可读性优势在配置文件多的时候很明显。4.4 坑四oh-package-lock.json5 被手动修改导致依赖树断裂现象ohpm install报lock file integrity check failed或者安装的包版本和 oh-package.json5 里声明的不一致。原因oh-package-lock.json5 是自动生成的锁文件记录了每个依赖的精确版本和哈希。手动改这个文件或者从别的项目拷贝过来会导致哈希对不上。解决删掉 oh-package-lock.json5重新执行ohpm install让工具根据 oh-package.json5 重新生成锁文件。如果 oh-package.json5 里的版本号写的是范围比如^1.0.0生成的锁文件会锁定到具体版本。想固定版本就直接写死别用范围符号。4.5 坑五资源文件放错目录导致图片加载不出来现象应用能跑但界面上的图标或者图片显示为空白日志里报resource not found。原因HarmonyOS NEXT 的资源引用有严格的目录约定。AppScope/resources 放全局资源entry/src/main/resources 放模块资源。图片要放在 resources/base/media 目录下引用时用$r(app.media.xxx)。如果图片放在 rawfile 目录引用方式不同要用$rawfile(xxx.png)。解决确认 png 文件的位置。这个工程有 4 个 png大概率在 entry/src/main/resources/base/media 下。引用时检查代码里用的是$r(app.media.文件名)还是$rawfile(文件名)两者不能混用。改完资源目录后执行一次 clean 再构建避免缓存导致资源没打包进去。5. 从跑通到改出自己东西三个进阶操作和一个验证习惯5.1 把 mock 数据换成真实接口数据这个工程自带 mock 目录里面的数据结构就是食谱的字段定义。你跑通之后第一件事应该是把 mock 数据替换成真实数据源。常见做法是在 entry/src/main/ets 下新建一个 services 目录写一个数据请求模块用ohos.net.http发请求然后把返回的 json 解析成 RecipeItem 数组。// services/recipe_service.ts import http from ohos.net.http; import { RecipeItem } from ../utils/recipe_utils; export async function fetchRecipes(): PromiseRecipeItem[] { const httpRequest http.createHttp(); try { const response await httpRequest.request( https://your-api.com/recipes, { method: http.RequestMethod.GET, header: { Content-Type: application/json }, connectTimeout: 10000, readTimeout: 10000 } ); if (response.responseCode 200) { return JSON.parse(response.result as string) as RecipeItem[]; } return []; } finally { httpRequest.destroy(); } }这段代码创建了一个 HTTP 请求GET 方法拉取食谱列表超时设了 10 秒。responseCode 为 200 时把结果解析成 RecipeItem 数组返回。finally 里销毁请求对象避免内存泄漏。注意 HarmonyOS NEXT 的网络请求需要申请 ohos.permission.INTERNET 权限在 module.json5 的 requestPermissions 里加上。5.2 用 ArkTS 的 State 和 Prop 做列表与详情联动食谱 App 的核心交互是列表点进去看详情。ArkTS 的状态管理用 State 和 Prop 装饰器。列表页维护一个 State 修饰的 recipes 数组点击某一项时把选中的 RecipeItem 通过 router 参数传给详情页详情页用 Prop 接收。// 列表页片段 Entry Component struct RecipeListPage { State recipes: RecipeItem[] []; build() { List() { ForEach(this.recipes, (item: RecipeItem) { ListItem() { Text(item.name) .fontSize(18) .onClick(() { router.pushUrl({ url: pages/RecipeDetailPage, params: { recipe: item } }); }) } }, (item: RecipeItem) item.id.toString()) } } }ForEach 的第三个参数是 key 生成函数用 id 的字符串形式保证唯一性。onClick 里用 router.pushUrl 跳转params 把整个 item 传过去。详情页在 aboutToAppear 生命周期里通过 router.getParams() 拿到参数赋值给 Prop 修饰的变量。这套流程和 Flutter 的 Navigator.push 加构造函数传参思路一致但 ArkTS 的 router 是全局的不需要 BuildContext。5.3 构建产物验证HAP 包里到底装了什么改完代码构建出 HAP 之后别急着装设备。先把 HAP 解压看一眼确认资源文件和代码都打进去了。HAP 本质是个 zip 包改后缀为 .zip 解压即可。# 把 HAP 复制一份改名为 zip cp entry/build/default/outputs/default/entry-default-signed.hap ./check.zip unzip -l check.zip # 输出会列出包内所有文件unzip -l列出包内文件清单。重点看三个东西ets 目录下的 .abc 文件ArkTS 编译后的字节码、resources 目录下的资源索引、module.json5 是否在根目录。如果 resources 目录是空的说明资源没打包进去回头检查 resources 目录结构和 build-profile.json5 里的资源配置。如果 .abc 文件缺失说明代码编译失败但构建没报错这种情况少见但遇到过一般是 hvigorfile.ts 里的编译任务被跳过了。从那以后我每次改完构建配置都强制走一遍「clean → install → assembleHap → 解压检查」的流程不省这一步。希望帮到你。本文还有配套的精品资源点击获取
返回列表