冰箱大厨:AI 智能菜谱推荐应用的鸿蒙实现

一、引言

在快节奏的现代生活中,很多人面临着一个共同的困境:冰箱里明明塞满了食材,打开冰箱门却不知道做什么菜。要么是食材搭配不均衡,要么是缺乏烹饪灵感,最终导致食材浪费。

"冰箱大厨"正是为解决这一痛点而生的鸿蒙原生AI应用。用户只需输入冰箱中现有的食材、口味偏好和用餐人数,AI便能智能推荐2道创意菜谱,包含详细的用料清单、烹饪步骤、替代食材建议和营养估算。本文将深入解析该应用的鸿蒙技术架构、AI应用亮点以及交互设计哲学。
在这里插入图片描述

二、应用架构设计

2.1 整体架构

冰箱大厨采用经典的鸿蒙声明式UI架构,遵循MVVM(Model-View-ViewModel)模式,将应用分为三个核心模块:

  • Model层(FridgeChefModel.ets):定义数据结构,包括 FCRecipe(菜谱)、RecipeData(菜谱集合)、FCMMessage(消息记录)等数据模型,以及常量配置如口味列表和欢迎语。
  • View层(FridgeChefPage.ets):基于ArkTS的声明式UI,使用 @Entry@Component 装饰器构建页面,通过 @State 管理响应式状态。
  • Service层(FridgeChefService.ets):封装AI生成逻辑,通过预设数据模拟AI生成的菜谱结果。

2.2 数据流设计

用户输入 → @State 状态更新 → 条件渲染触发 → 调用Service → 更新数据 → 结果展示

核心数据流为单向流动:用户通过 TextInput 和按钮交互修改 @State 变量,系统自动触发UI重渲染,点击"生成菜谱"按钮调用Service层获取数据,最终通过 @Builder 构建的菜谱卡片展示结果。

三、鸿蒙技术深度解析

3.1 @State 声明式状态管理

鸿蒙ArkTS框架的核心特性之一是 @State 装饰器,它实现了状态的响应式管理。在冰箱大厨中,我们使用了多个 @State 变量:

@State messages: FCMMessage[] = []
@State selectedIngredients: string = ''
@State selectedTaste: string = ''
@State selectedPeople: number = 2
@State currentData: RecipeData | null = null
@State isLoading: boolean = false

每个 @State 变量都是UI的"单点真理"。当用户输入食材时,selectedIngredients 通过 onChange 回调实时更新,而UI中所有依赖该变量的部分(如"生成菜谱"按钮的条件渲染)会自动重新渲染,无需手动操作DOM。这种响应式编程范式大幅降低了状态管理的复杂度,杜绝了数据不一致的问题。

3.2 @Builder 自定义构建函数——菜谱卡片组件化

@Builder 是鸿蒙声明式UI中实现代码复用的核心机制。冰箱大厨中定义了两个 @Builder 函数:

@Builder
buildHeader() {
  // 顶部标题栏,包含图标、标题、副标题和重置按钮
}

@Builder  
buildResultCard(data: RecipeData) {
  // 菜谱结果卡片,包含菜名、用时、难度、用料、步骤、营养信息
}

buildResultCard 是应用中最复杂的UI组件,它使用 ForEach 循环渲染多道菜谱:

ForEach(data.recipes, (recipe: FCRecipe, idx: number) => {
  Column() {
    // 菜名 + 序号
    // 用时 + 难度标签
    // 用料列表(ingredients.join('、'))
    // 步骤列表(带序号)
    // 营养信息(热量、蛋白质)
  }
})

通过 @Builder 将菜谱卡片封装为独立组件,不仅使主 build() 方法的代码更加简洁清晰,还方便了后续的维护和扩展。如果需要调整菜谱卡片的样式,只需修改 buildResultCard 一处即可。

3.3 ForEach 高效列表渲染

ForEach 是鸿蒙开发中用于遍历数组并动态生成UI组件的核心API。在本应用中,ForEach 被用于两个场景:

  1. 菜谱列表渲染:遍历 data.recipes 数组,为每道菜生成独立的卡片
  2. 步骤列表渲染:遍历 recipe.steps 数组,为每个步骤生成带有序号的文本行
ForEach(recipe.steps, (step: string, si: number) => {
  Text(`${si + 1}. ${step}`)
    .fontSize(12)
    .fontColor(COLOR_TEXT_SEC)
    .padding({ left: 8, top: 2, bottom: 2 })
})

ForEach 的第二个参数提供了索引 si,这使得我们可以为每个步骤显示序号(1. 2. 3. …),而不需要额外维护计数器变量。鸿蒙的 ForEach 还支持高效的列表差异化更新,当数据变化时只重新渲染发生变化的项,而非整个列表。

3.4 Scroll 滚动容器与条件渲染

由于菜谱内容可能超出屏幕高度,应用使用 Scroll 容器包裹结果区域:

if (this.currentData !== null) {
  Scroll() {
    Column() {
      this.buildResultCard(this.currentData)
    }
    .padding({ bottom: 20 })
  }
  .layoutWeight(1)
  .scrollBar(BarState.Off)
}

layoutWeight(1) 让Scroll容器占据剩余空间,scrollBar(BarState.Off) 隐藏滚动条以保持界面简洁。与条件渲染 if (this.currentData !== null) 结合,实现了"有结果才显示"的优雅交互——在没有生成结果时,该区域完全不可见,不占用布局空间。

3.5 样式常量与主题一致性

应用采用常量定义颜色方案,确保整个应用的主题一致性:

const COLOR_BG = '#F0FDF4'
const COLOR_CARD = '#FFFFFF'
const COLOR_PRIMARY = '#22C55E'  // 绿色主题
const COLOR_SELECTED_BG = '#DCFCE7'

绿色主题与"厨师"和"食材"的概念高度契合,营造出新鲜、健康的视觉感受。

四、AI应用亮点分析

4.1 仅用所列食材+常见调料的智能搭配

冰箱大厨的核心AI能力在于"物尽其用"。用户输入的食材将被作为菜谱的核心用料,AI仅在此基础上添加常见调料(盐、糖、生抽、油等),确保每道菜都能用冰箱里的食材做出来,同时避免为了做一道菜而额外购买大量食材。

4.2 缺料自动给替代建议

当某道菜谱需要使用用户可能没有的食材时,AI会智能提供替代方案:

substitutes: { '鸡胸肉': '五花肉', '莲藕': '花菜' }

替代建议被设计为 Record<string, string> 类型,key为原食材,value为替代食材。这种设计让用户即使缺少某种食材,也能灵活调整,大大降低了烹饪门槛。

4.3 营养估算

每道菜谱都包含热量和蛋白质估算:

nutrition: { 'kcal': '380', 'protein': '28g' }

这帮助用户在享受美食的同时,对每餐的营养摄入有直观的了解,特别适合有健康管理需求的用户。

4.4 5种口味预设

应用支持5种口味(麻辣、清淡、酸甜、咸香、家常),覆盖了中餐最主流的味型。每种口味对应2道特色菜谱,用户可以根据自己的喜好灵活选择。

五、关键技术挑战与解决方案

5.1 挑战一:输入状态的联动控制

问题:生成菜谱按钮需要同时满足"食材不为空"和"口味不为空"两个条件。

解决方案:使用条件运算符组合判断:

if (this.selectedIngredients !== '' && this.selectedTaste !== '') {
  Text('生成菜谱')
    // 样式配置
    .onClick(() => { this.onGenerate() })
}

当任一条件不满足时,按钮直接不渲染,从UI层面杜绝了无效操作。

5.2 挑战二:人数选择的边界控制

问题:用餐人数需要在1-10人之间,且不能出现无效值。

解决方案:在加减按钮的 onClick 中设置边界检查,同时使用 FlexAlign.Center 居中布局:

Button('-').onClick(() => { if (this.selectedPeople > 1) { this.selectedPeople-- } })
Button('+').onClick(() => { if (this.selectedPeople < 10) { this.selectedPeople++ } })

5.3 挑战三:加载状态的用户体验

问题:AI生成菜谱需要一定时间,需要给用户明确的反馈。

解决方案:使用 isLoading 状态控制加载提示的显示,并通过 setTimeout 模拟AI处理延迟:

this.isLoading = true
setTimeout(() => {
  const data = this.service.getRecipe(...)
  this.currentData = data
  this.isLoading = false
}, 1500)

加载期间显示"👨‍🍳 正在搭配菜谱……"的提示,让用户感知到系统正在工作。

六、用户体验设计

6.1 极简交互流程

冰箱大厨的交互流程只有三步:

  1. 输入食材:在TextInput中输入,逗号分隔
  2. 选择口味:输入口味偏好关键词
  3. 调整人数:通过加减按钮调整

三个步骤清晰明了,用户无需学习成本即可上手。

6.2 视觉反馈设计

  • 重置按钮:当用户有输入时显示,方便一键清空所有内容
  • 加载动画:处理期间的文字提示让用户感知到系统正在工作
  • 结果卡片:采用绿色主题卡片展示,清晰区分菜名、用料、步骤和营养信息

6.3 信息层级设计

结果卡片采用清晰的信息层级:

  1. 标题层级:🍽️ 推荐菜谱 → 序号+菜名
  2. 元信息:⏱ 用时 + 📊 难度
  3. 详细内容:🥬 用料 → 📝 步骤 → 📊 营养

这种层级设计让用户先看到最重要的信息(菜名和难度),再逐步深入到细节(步骤和营养)。

七、总结

冰箱大厨作为一款鸿蒙原生AI应用,充分展示了ArkTS声明式UI框架在构建实用工具类应用上的优势。通过 @State 响应式状态管理、@Builder 组件化构建、ForEach 高效列表渲染等核心技术,我们构建了一个交互流畅、视觉清新的智能菜谱推荐工具。

在AI能力方面,应用通过食材限制、替代建议和营养估算三大特色功能,切实解决了用户"不知道做什么菜"的日常痛点。未来可以进一步扩展AI能力,如支持拍照识别食材、根据季节推荐时令菜谱、集成购物清单生成等。

更重要的是,这款应用展示了鸿蒙生态中AI应用的发展方向——将强大的AI能力融入日常生活的每一个细节,让技术真正服务于人。


技术栈:HarmonyOS ArkTS | 声明式UI | @State | @Builder | ForEach | Scroll | Flex
应用类型:AI + 生活工具
适用场景:日常烹饪、食材管理、健康饮食规划

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐