鸿蒙原生实战 008:ArkUI+ArkTS 从零开发高颜值菜谱 App
一、项目前言
HarmonyOS NEXT 纯血鸿蒙系统现已全面商用落地,ArkUI 声明式 UI + ArkTS 强类型语言 是官方统一标准开发技术栈。目前网络上大部分鸿蒙入门教程仅拆分单个组件语法进行讲解,知识点碎片化严重,缺少一套具备完整业务流程、标准化工程结构、现代化视觉界面、完善异常容错处理的综合实战案例。
对于计算机、物联网专业学生来说,鸿蒙课程设计、期末实训作业极易出现功能残缺、界面粗糙、代码无注释、无性能优化、缺少问题解决方案等扣分点。
本文带来轻量化菜谱查询应用完整实战项目,适配 HarmonyOS NEXT API20 及以上全部新版本,全程不引入任何第三方依赖库、无外部插件,全部功能依托系统原生 API 实现。项目覆盖生活服务类 App 全部高频开发场景:横向菜系分类筛选、输入框实时模糊检索、路由安全跨页传参、详情页分层数据展示、难度等级动态配色、空状态兜底渲染、多形式条件渲染、多条件复合数据过滤、自适应卡片列表布局等课程核心考点。
二、项目整体设计方案
2.1 开发背景与需求调研
现有方案痛点分析
传统纸质菜谱:检索效率低下,无法按照菜系、制作难度分类,缺少预估烹饪时长,携带、保存都极为不便;
市面商用美食软件:功能繁杂冗余,充斥大量开屏广告、推送弹窗,后台常驻进程占用高额内存,仅简单查菜谱场景使用成本过高;
开源鸿蒙简易 Demo:业务逻辑大幅简化,缺少参数容错机制,UI 未遵循鸿蒙官方设计规范,无性能优化相关代码,无法直接作为课程设计提交。
本项目解决方案
基于 HarmonyOS NEXT API20 原生技术栈开发轻量化菜谱查询工具,依托 ArkUI 数据驱动视图机制实现毫秒级列表刷新,借助 ArkTS 静态类型校验在编译阶段拦截绝大多数运行报错。应用主打低功耗、无广告、轻量化交互,精准匹配居家自学烹饪、快速检索菜品等轻量使用场景,视觉规范、交互逻辑完全对齐华为鸿蒙人机界面设计标准。
2.2 核心使用场景
基础已落地场景(项目原生完整实现)
新手烹饪学习:首页卡片快速预览菜品基础信息,点击一键跳转详情页,查看完整食材清单与分步烹饪教程;
双向模糊检索:支持菜名、食材关键词双重检索,自动兼容大小写、清除首尾空格,检索容错性拉满;
菜系定向筛选:内置家常菜、川菜、粤菜三大分类标签,点击标签实时刷新对应菜品列表;
难度可视化区分:简单 / 中等 / 困难三级难度搭配差异化主题色展示,同步标注菜品预估制作时长;
多终端自适应:页面布局自动适配手机、平板不同尺寸屏幕,卡片组件流式缩放,不存在布局错乱、内容截断问题。
拓展增值场景(课程设计高分加分模块,可自主二次开发)
菜谱本地收藏持久化、搜索历史记录缓存、食材购物清单一键导出、菜品营养参数展示、长列表懒加载优化、自定义新增菜谱功能。
2.3 全量功能特性清单
✅ 遵循鸿蒙设计规范的圆角阴影卡片列表,分层视觉设计,自适应流式布局
✅ 多标签联动筛选逻辑,复合条件过滤算法,状态变更实时响应刷新页面视图
✅ TextInput 实时输入监听检索,自动去除无效空格、大小写兼容,检索交互流畅无卡顿
✅ 标准 router 导航管理体系,参数空值校验、基础类型序列化传输,杜绝页面崩溃、数据丢失问题
✅ 详情页模块化分层布局:顶部返回导航栏、菜品基础信息、可滚动食材区、分步烹饪教程区
✅ 动态绑定难度等级主题色,经典 ArkUI 动态样式开发案例,可复用至各类标签、状态组件
✅ 完善空数据兜底页面,无检索结果、无对应分类菜品时展示友好引导文案,完善应用容错逻辑
✅ ArkTS 强类型实体类统一管理全局数据源,代码解耦度高,便于后期迭代维护
✅ Scroll 弹性滚动容器,搭配 Spring 弹簧回弹动效,完美解决超长文本屏幕溢出、内容截断问题
✅ 三类主流条件渲染方案完整落地,覆盖大面积布局切换、单行文本状态、局部组件显隐全场景
✅ 全代码添加空数组、空参数判断逻辑,程序健壮性强,无运行时异常报错
2.4 项目技术栈明细
表格
技术模块 选型与详细说明
系统适配版本 HarmonyOS NEXT API 20 及以上,兼容 API21、API22 新版本,无废弃 API 警告,向下兼容性优秀
开发语言 ArkTS,鸿蒙官方强类型开发语言,基于 TypeScript 拓展,编译期完成类型校验,大幅减少闪退问题
UI 开发框架 ArkUI 声明式开发范式,数据驱动自动刷新视图,渲染性能优于传统命令式布局
核心基础组件 List、ForEach、TextInput、Scroll、Column/Row 弹性布局、Blank 空白占位组件
系统核心 API @ohos.router 页面路由、数组 filter/some 高阶方法、字符串标准化处理、@State 响应式状态管理
状态管理方案 @State 组件内响应式状态,适配双页面小型应用;拓展可接入 @Observed 全局状态管理
第三方依赖 零外部依赖,仅使用系统内置原生 API,项目安装包体积轻量化,不存在版本冲突、打包编译报错
2.5 页面分层架构设计
整体采用低耦合双页面轻量化架构,每个页面职责单一清晰,完全符合鸿蒙工程分层开发思想:
首页 Index.ets(业务展示层)
顶部标题栏:应用名称居中展示,统一页面顶部视觉结构
全局搜索模块:标准化圆角输入框,实时监听输入内容自动触发数据检索
横向可滑动分类标签栏:切换菜系分类,联动筛选菜谱数据源
菜谱卡片列表区:循环渲染菜品卡片,点击卡片携带参数跳转详情页面
空状态提示区:筛选无匹配数据时展示友好引导文案
详情页 RecipeDetail.ets(数据详情展示层)
顶部导航栏:左侧返回按钮 + 菜品名称标题
菜品基础信息模块:菜系标签、制作时长、动态配色难度标识
食材滚动容器:垂直弹性滚动容器,完整展示全部原材料清单
分步烹饪教程区:有序逐条展示完整制作流程,排版清晰易读
三、核心技术知识点深度解析(课程设计高分核心考点)
本章节区别于网络浅层入门教程,不单纯堆砌代码片段,同步讲解底层运行原理、开发踩坑根源、落地优化方案,是评审老师重点审阅板块,拉开作业分数差距的核心内容。
3.1 页面路由与跨页面安全传参(期末必考核心知识点)
多页面应用开发的基础核心能力,实现首页菜品卡片跳转详情、携带菜品唯一标识参数,详情页解析参数渲染对应菜品数据,同时管理页面返回栈,完整覆盖 router 四大核心 API 使用场景与底层逻辑。
四大路由 API 适用场景详解
router.pushUrl ():新增页面入栈,保留当前页面浏览历史,列表跳转详情标准用法,支持返回上一级页面;
router.back ():弹出当前页面,路由栈出栈,回到上一级浏览页面;
router.replaceUrl ():替换当前栈顶页面,不保留历史记录,多用于登录页跳转首页场景;
router.getParams ():读取上级页面传递参数,仅支持基础数据类型序列化传输。
完整实战标准代码(附带类型约束与空值安全校验)
typescript
运行
// 导入系统路由核心模块
import router from '@ohos.router'
import type { Recipe } from '../model/Recipe'
/**
* 跳转菜谱详情页,仅传递基础类型ID与菜名,规避复杂对象传输异常
* @param recipe 当前点击选中的菜谱实体对象
*/
private jumpToDetail(recipe: Recipe) {
router.pushUrl({
url: 'pages/RecipeDetail',
params: {
recipeId: recipe.id,
recipeName: recipe.name
}
})
}
详情页接收参数代码:
typescript
运行
@State currentRecipeId: number = 0
@State currentRecipeName: string = ''
aboutToAppear() {
// 强类型转换+双重空值拦截,防止空指针导致程序崩溃
const params = router.getParams() as Record<string, Object>
if (params && Reflect.has(params, 'recipeId')) {
this.currentRecipeId = params.recipeId as number
this.currentRecipeName = params.recipeName as string
}
}
// 返回首页
private backPrevPage() {
router.back()
}
高频踩坑优化方案
禁止直接传递 Recipe 实体对象:路由参数仅支持 number、string、boolean 基础类型,复杂对象传输会出现参数丢失、页面闪退;
参数读取必须做空值判断:低版本 API 存在 getParams 返回 undefined 情况,无校验直接取值会触发空指针异常;
页面路径严格匹配工程目录:路径大小写敏感,书写错误会导致跳转无响应。
3.2 TextInput 实时检索搜索栏封装(交互优化核心考点)
基于原生 TextInput 封装一体化搜索组件,通过 onChange 实时监听输入内容,实现边输入边筛选。组件尺寸、圆角、内边距严格遵循鸿蒙触控控件规范,优化原生输入框生硬的视觉缺陷。
typescript
运行
@State searchKeyword: string = ''
TextInput({
placeholder: '请输入菜谱名/食材关键词搜索',
text: this.searchKeyword
})
.width('100%')
.height(44)
.backgroundColor(Color.White)
.borderRadius(22)
.padding({ left: 16, right: 16 })
// 输入实时触发数据过滤,刷新列表视图
.onChange((value: string) => {
this.searchKeyword = value
this.getFilteredRecipes()
})
检索前自动 trim 去除首尾空白字符,屏蔽无效空格检索;
44px 固定高度适配手指触控,符合鸿蒙无障碍交互标准;
可拓展增加软键盘回车检索、一键清空输入框功能。
3.3 Scroll 弹性滚动容器布局方案(布局容错必备技术)
食材清单、烹饪步骤文本长度不固定,极易出现内容溢出屏幕、布局截断问题。使用垂直 Scroll 容器承载超长文本,搭配 Spring 弹性回弹动效,大幅提升滑动交互质感。
typescript
运行
Scroll() {
Column() {
ForEach(this.currentIngredients, (item: string) => {
Text(`• ${item}`)
.fontSize(15)
.padding({ top: 6, bottom: 6 })
})
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.edgeEffect(EdgeEffect.Spring) // 鸿蒙专属弹簧滚动效果
3.4 多条件复合数据过滤(项目核心业务算法,答辩重点讲解)
项目核心业务逻辑,整合菜系分类筛选 + 关键词模糊检索双重过滤条件,同时支持菜名、食材数组双向关键词匹配,统一全部文本小写处理,实现检索不区分大小写。算法分层解耦,后续可快速新增时长、难度等多维度筛选逻辑。
typescript
运行
// 当前用户选中的菜系分类标签
@State selectedCategory: string = '全部'
/**
* 复合条件过滤菜谱数据源,返回可直接渲染的菜品数组
* @returns 筛选完成后的菜谱列表
*/
private getFilteredRecipes(): Recipe[] {
let resultList = [...this.recipes] // 浅拷贝原始数据,避免污染源数组
// 条件1:按菜系分类过滤数据
if (this.selectedCategory !== '全部') {
resultList = resultList.filter(item => item.category === this.selectedCategory)
}
// 条件2:关键词模糊检索,统一小写消除大小写差异
const keyword = this.searchKeyword.trim().toLowerCase()
if (keyword) {
resultList = resultList.filter(item => {
// 匹配规则:菜名包含关键词 或 任意食材包含关键词
return item.name.toLowerCase().includes(keyword)
|| item.ingredients.some(ing => ing.toLowerCase().includes(keyword))
})
}
return resultList
}
算法优化亮点
分层过滤逻辑,先分类、后检索,减少数组循环遍历次数,降低页面渲染开销;
使用数组 some 原生方法遍历食材,性能优于手动循环遍历;
文本标准化统一小写处理,解决大小写检索失效的用户体验缺陷。
3.5 动态样式绑定:难度等级差异化配色
根据菜品难度等级动态渲染文字颜色,采用 switch 分支完成三级难度色彩区分,是 ArkUI 动态样式开发经典案例,可复用在标签、状态、评级类全部场景。
运行
/**
* 根据难度等级返回对应视觉主题色
* @param difficulty 菜品难度:简单/中等/困难
* @returns 十六进制颜色字符串
*/
private getDifficultyColor(difficulty: string): string {
switch (difficulty) {
case '简单':
return '#10b981' // 绿色,低难度视觉标识
case '中等':
return '#f59e0b' // 橙黄色,中等难度中性标识
case '困难':
return '#ef4444' // 红色,高难度强提醒标识
default:
return '#666666' // 默认灰色兜底
}
}
3.6 ArkUI 三类条件渲染完整落地(考点汇总)
项目完整实现鸿蒙开发中三种主流条件渲染写法,适配不同业务场景,逻辑简洁、可维护性高,可单独整理进课程报告知识点总结板块:
if/else 分支渲染:用于大面积布局切换(空状态 / 正常列表)
typescript
运行
if (this.getFilteredRecipes().length === 0) {
Text('暂无匹配菜谱,请更换关键词或分类')
.fontSize(16)
.fontColor('#999')
.margin({ top: 40 })
} else {
List() {
ForEach(this.getFilteredRecipes(), (recipe: Recipe) => {
// 菜谱卡片完整布局代码
})
}
}
三元运算符渲染:单行文本、简单数字状态切换
typescript
运行
Text(this.isLoading ? '加载中...' : `共${this.getFilteredRecipes().length}道菜谱`)
短路 && 运算渲染:局部组件按需显隐,轻量化状态展示
typescript
运行
{this.searchKeyword && Text(`正在搜索:${this.searchKeyword}`).fontColor('#666')}
3.7 分层卡片式自适应布局(UI 设计高分核心)
严格遵循鸿蒙极简视觉规范,上下分层卡片结构,搭配圆角分层、柔和阴影、标准化留白,视觉层次感强,自适应屏幕宽度,该卡片模板可直接复用至商城、资讯、工具类所有列表项目。
typescript
运行
Column() {
// 卡片头部菜品标题展示区域
Column() {
Text(recipe.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
.width('100%')
.height(140)
.backgroundColor('#f97316')
.justifyContent(FlexAlign.Center)
.borderRadius({ topLeft: 12, topRight: 12 })
// 卡片底部菜品信息描述区域
Column() {
Row() {
Text(recipe.category)
.fontSize(12)
.backgroundColor('#f1f5f9')
.borderRadius(4)
.padding({ left: 8, right: 8 })
Text(recipe.difficulty)
.fontSize(12)
.fontColor(this.getDifficultyColor(recipe.difficulty))
.margin({ left: 10 })
}
Text(`制作时长:${recipe.cookTime}分钟`)
.fontSize(14)
.fontColor('#666')
.margin({ top: 8 })
}
.padding(12)
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 6, color: '#1a000000', offsetX: 2, offsetY: 2 })
.margin({ bottom: 12 })
四、标准化数据结构与业务逻辑实现
4.1 ArkTS 强类型菜谱实体类封装(工程化核心标准)
使用 class 封装标准化菜品数据模型,强类型约束所有字段数据类型,统一全局数据规范,从根源解决弱类型语言数据错乱、类型不匹配问题,是高分规范代码必备要求。
typescript
运行
/**
* 菜谱实体数据模型,全局统一数据结构定义
*/
class Recipe {
id: number; // 菜品唯一标识ID,用于路由匹配、数据区分
name: string; // 菜品中文名称
category: string; // 菜品所属菜系分类
ingredients: string[]; // 原材料食材数组
steps: string[]; // 分步烹饪流程数组
cookTime: number; // 预估制作总时长,单位:分钟
difficulty: string; // 烹饪难度等级:简单/中等/困难
image: string; // 菜品图片资源标识,拓展可绑定本地图片资源
constructor(id: number, name: string, category: string, ingredients: string[], steps: string[], cookTime: number, difficulty: string, image: string) {
this.id = id;
this.name = name;
this.category = category;
this.ingredients = ingredients;
this.steps = steps;
this.cookTime = cookTime;
this.difficulty = difficulty;
this.image = image;
}
}
4.2 页面初始化完整模拟数据源
页面生命周期 aboutToAppear 阶段加载多菜系、多难度测试数据,完整覆盖所有筛选、检索业务场景,测试用例齐全,评审老师可直观测试全部功能。
typescript
运行
@State recipes: Recipe[] = []
aboutToAppear() {
// 初始化多菜系测试菜谱数据,覆盖全部难度等级
this.recipes = [
new Recipe(
1,
'红烧肉',
'家常菜',
['五花肉500g', '生姜3片', '葱2根', '八角2个', '冰糖适量', '生抽2勺', '老抽1勺', '料酒1勺'],
['五花肉切块,冷水下锅焯水去除血沫', '锅中少油,加入冰糖炒出焦糖色', '放入五花肉快速翻炒上色', '加入葱姜八角和适量热水', '大火烧开转小火慢炖60分钟', '大火收汁,翻炒均匀即可出锅'],
90,
'中等',
'hongshaorou'
),
new Recipe(
2,
'宫保鸡丁',
'川菜',
['鸡胸肉300g', '花生米50g', '干辣椒5个', '花椒10粒', '葱姜蒜适量', '黄瓜半根'],
['鸡胸肉切丁,加盐、料酒腌制15分钟', '冷油下锅炸熟花生米备用', '锅中爆香花椒、干辣椒、葱姜蒜', '下入鸡丁翻炒至变色', '加入黄瓜丁、调料翻炒入味', '最后加入花生米翻炒均匀出锅'],
30,
'简单',
'gongbaojiding'
),
new Recipe(
3,
'白切鸡',
'粤菜',
['三黄鸡1只', '生姜5片', '葱3根', '料酒2勺', '沙姜适量', '生抽少许'],
['鸡肉处理干净,冷水浸泡去血水', '锅中烧水,加入葱姜料酒', '水微沸后放入鸡肉浸煮20分钟', '关火焖10分钟充分入味', '捞出过冰水,肉质更紧实', '切块搭配沙姜酱汁即可食用'],
40,
'简单',
'baiqieji'
)
]
}



五、项目高频故障与精细化性能优化方案
普通入门教程仅完成基础功能开发,本章节完整记录故障复现现象、底层产生根源、可直接落地的优化代码,写入课程报告可大幅提升综合评分。
问题 1:搜索关键词大小写敏感,匹配成功率低
故障现象:输入大写文字、字母关键词无法匹配菜品,检索容错性差;
底层根源:字符串 includes 匹配严格区分大小写,未对文本做统一标准化处理;
优化方案:检索关键词、菜名、食材全部统一转为小写后再执行匹配逻辑。
问题 2:路由跳转参数丢失、详情页空白、应用闪退
故障现象:点击卡片跳转详情页面无数据展示,极端场景直接程序崩溃;
底层根源:①直接传输完整 Recipe 实体对象,路由仅支持基础类型序列化;②读取参数无空值判断,读取 undefined 属性触发空指针;
优化方案:仅传递 id、name 基础参数;参数读取增加判空拦截,设置默认兜底值。
问题 3:切换分类标签,菜谱列表无刷新,筛选功能失效
故障现象:切换不同菜系标签,列表卡片内容无任何变化;
底层根源:ForEach 循环绑定原始固定数组 this.recipes,未绑定实时过滤后的动态结果;
优化方案:列表渲染直接绑定 getFilteredRecipes () 动态计算数组,状态变更自动刷新页面视图。
问题 4:筛选无匹配数据时页面纯白,用户交互体验极差
故障现象:无检索结果、无对应分类菜品时页面空白,无任何引导提示;
优化方案:通过 if 条件渲染空状态提示文案,完善应用全链路容错逻辑,降低用户使用困惑。
拓展性能优化加分点(可写入实训报告提升评分)
长列表将 ForEach 替换为 LazyForEach,实现视图懒加载,滑动内存占用降低 40%;
页面销毁时清空搜索关键词、选中分类状态,避免页面缓存数据错乱;
检索前增加 trim 去除首尾空格,屏蔽空白字符无效检索;
循环渲染绑定唯一 key 标识,减少 ArkUI 视图重复重绘开销。
更多推荐


所有评论(0)