营养餐单规划:AI 科学饮食规划系统的鸿蒙实现

一、引言

随着健康意识的提升,越来越多人开始关注自己的饮食结构和营养摄入。然而,科学地规划每日餐单并非易事——需要计算基础代谢率、确定目标热量、合理分配宏量营养素,还要考虑个人忌口和饮食偏好。

"营养餐单规划"是一款基于鸿蒙系统的AI营养餐单生成应用,用户只需输入身高、体重、选择目标(减脂/增肌/维持)和忌口食材,AI便能科学估算目标热量和宏量营养素分配,并生成完整的三餐分配方案。本文将深入解析该应用的鸿蒙技术实现、AI营养科学算法和交互设计。
在这里插入图片描述

二、应用架构设计

2.1 整体架构

营养餐单规划采用鸿蒙标准的MVVM三层架构:

  • Model层(MealPlanModel.ets):定义 Macros(宏量营养素)、MealEntry(餐次)、MealPlanData(完整餐单数据)等数据模型,以及目标选项和忌口选项的常量配置。
  • View层(MealPlanPage.ets):使用 @Entry@Component 构建UI,通过 @State 管理身高、体重、目标、忌口等表单状态。
  • Service层(MealPlanService.ets):封装营养计算逻辑,根据用户参数返回科学配比的餐单数据。

2.2 数据模型设计

MealPlanData
├── target_kcal: number      // 目标热量
├── macros: Macros           // 宏量营养素分配
│   ├── protein_g: number    // 蛋白质(g)
│   ├── carb_g: number       // 碳水化合物(g)  
│   └── fat_g: number        // 脂肪(g)
└── meals: MealEntry[]       // 三餐+加餐
    ├── type: string         // 餐次类型
    ├── foods: string[]      // 食物列表
    └── kcal: number         // 该餐热量

三、鸿蒙技术深度解析

3.1 Flex 弹性布局实现胶囊式选择器

鸿蒙的 Flex 组件是实现响应式布局的利器。在营养餐单规划中,我们使用 Flex 配合 FlexWrap.Wrap 实现了目标选择和忌口多选的胶囊式UI:

Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
  ForEach(items, (item: string) => {
    Text(item)
      .fontSize(14)
      .fontWeight(selected === item ? FontWeight.Bold : FontWeight.Normal)
      .fontColor(selected === item ? COLOR_PRIMARY : COLOR_TEXT)
      .padding({ left: 14, right: 14, top: 8, bottom: 8 })
      .backgroundColor(selected === item ? COLOR_SELECTED_BG : COLOR_CARD)
      .borderRadius(16)
      .border({ width: 1, color: selected === item ? COLOR_PRIMARY : COLOR_BORDER })
      .margin({ right: 8, bottom: 8 })
      .onClick(() => { onClick(item) })
  })
}

FlexWrap.Wrap 使得选项在容器宽度不足时自动换行,避免了水平滚动。每个选项通过 borderRadius(16) 呈现胶囊形状,选中态通过颜色、边框和字体粗细的变化清晰反馈。这种设计在移动端非常常见,具有良好的触摸友好性。

3.2 @Builder 复用——单选与多选组件的差异化实现

营养餐单规划中定义了两种选择器组件:buildSection(单选)和 buildMultiSection(多选),它们通过 @Builder 实现模板化复用。

单选组件:目标选择(减脂/增肌/维持)

@Builder
buildSection(title: string, items: string[], selected: string, onClick: (item: string) => void) {
  // 使用 selected === item 判断选中状态
}

多选组件:忌口选择(海鲜/花生/牛奶/鸡蛋/麸质/大豆)

@Builder
buildMultiSection(title: string, items: string[], selected: string[]) {
  // 使用 selected.indexOf(item) >= 0 判断选中状态
  // 点击时通过 splice 或 push 增删
}

两者的核心差异在于选中状态的判断逻辑和点击处理方式。多选组件通过 indexOf 检测当前项是否在 selected 数组中,点击时使用 splice 移除或 push 添加。这种设计模式可以轻松扩展到更多选择场景。

3.3 @State 深度响应式——数组状态的变更检测

值得注意的是,在多选组件中,我们直接修改了 @State 数组的内容:

.onClick(() => {
  const idx = selected.indexOf(item)
  if (idx >= 0) {
    selected.splice(idx, 1)  // 移除
  } else {
    selected.push(item)       // 添加
  }
})

鸿蒙的 @State 装饰器对数组的 splicepush 操作具有深度响应能力——当数组内容发生变化时,所有依赖该数组的UI部分会自动重渲染。这与某些框架要求"替换整个数组"的做法不同,鸿蒙的响应式系统更加智能和高效。

3.4 Divider 分割线优化信息层级

在结果卡片中,我们使用 Divider 组件来分隔不同的信息区块:

Divider().margin({ top: 12, bottom: 12 })

Divider 在营养目标、三餐安排等不同区块之间形成视觉分隔,让用户能够清晰地识别信息结构。配合 margin 的间距控制,避免了信息堆砌带来的视觉疲劳。

3.5 条件渲染与按钮可用性控制

生成按钮的可用性由三个条件共同决定:

if (this.selectedHeight !== '' && this.selectedWeight !== '' && this.selectedGoal !== '') {
  Text('生成餐单')
    // 样式与点击事件
}

这种"不满足条件则不渲染"的策略,比"禁用按钮+灰色样式"更加直接——用户不会因为点击了不可用的按钮而感到困惑。

四、AI应用亮点分析

4.1 目标热量科学估算

营养餐单规划的核心AI能力在于基于用户身体数据科学估算目标热量。根据不同的目标,系统采用不同的热量策略:

目标 热量策略 示例(中等体型)
减脂 热量缺口(约-300~500kcal) 1600 kcal
增肌 热量盈余(约+300~500kcal) 2800 kcal
维持 维持基础代谢+活动消耗 2200 kcal

4.2 宏量营养素合理分配

不同目标对应不同的宏量营养素比例:

  • 减脂:高蛋白(120g)+ 低碳水(150g)+ 低脂(50g),蛋白质占比约30%,有助于维持肌肉同时减少脂肪
  • 增肌:超高蛋白(180g)+ 高碳水(350g)+ 适中脂肪(70g),蛋白质占比约26%,为肌肉合成提供充足原料
  • 维持:均衡配比(蛋白质140g + 碳水260g + 脂肪60g)

4.3 忌口食材自动避让

当用户选择了忌口食材(如海鲜、花生、牛奶等),系统在生成餐单时会自动避开含这些食材的食物。例如,如果用户选择了"牛奶"作为忌口,早餐中的牛奶会被替换为豆浆等替代品。

4.4 四餐分配方案

系统将每日热量合理分配到三餐加一餐加餐:

  • 早餐:约占25%(如400-650 kcal)
  • 午餐:约占30-35%(如480-750 kcal)
  • 晚餐:约占25-30%(如420-680 kcal)
  • 加餐:约占10-15%(如180-420 kcal)

这种分配方式符合"早吃好、午吃饱、晚吃少"的健康饮食原则,同时加餐的设置可以有效避免饥饿感。

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

5.1 挑战一:多选状态管理的性能优化

问题:多选组件中,每次点击都需要遍历数组进行 indexOf 查找,当选项较多时可能影响性能。

解决方案:使用 indexOf 进行线性查找,对于6个忌口选项来说性能足够。如果未来扩展更多选项,可以考虑使用 Set 数据结构优化:

// 优化方案
const avoidSet = new Set(this.selectedAvoid)
// 判断时:avoidSet.has(item)
// 增删时:avoidSet.add(item) / avoidSet.delete(item)

5.2 挑战二:信息展示的优先级控制

问题:餐单结果需要展示热量、宏量营养素、三餐详情等多个维度的信息,如何组织才能让用户快速获取关键信息?

解决方案:采用"总-分"的信息架构:

  1. 总览:最上方展示目标热量(大号字体突出显示)
  2. 宏量营养素:以标签形式展示蛋白质、碳水、脂肪的具体克数
  3. 详情:通过 ForEach 循环展示三餐的具体食物和热量

5.3 挑战三:重置功能的完整性

问题:重置时不仅要清空输入,还要清空消息记录和结果数据。

解决方案:在 onReset 方法中,将所有 @State 变量重置为初始值,并重新添加欢迎消息:

private onReset(): void {
  this.selectedHeight = ''
  this.selectedWeight = ''
  this.selectedGoal = ''
  this.selectedAvoid = []
  this.currentData = null
  this.messages = []
  this.messages.push(new MPMMessage(MPMMessageRole.ASSISTANT, MPM_WELCOME, null))
}

六、用户体验设计

6.1 橙色主题与健康感知

应用采用橙色主题(#F97316),橙色在色彩心理学中代表活力、温暖和健康,与"营养餐单"的应用场景高度契合。背景色 #FFF7ED 是浅橙色,与主色形成渐变过渡,营造出舒适、温暖的视觉氛围。

6.2 渐进式表单填写

用户依次填写身高 → 体重 → 目标 → 忌口,每一步都是独立的输入区块,视觉上通过间距和标题清晰分隔。这种渐进式设计避免了将所有输入堆砌在一起带来的压迫感。

6.3 结果卡片的信息密度控制

结果卡片采用"标题+标签+列表"的组合:

  • 大标题展示总热量
  • 彩色标签展示宏量营养素
  • 循环卡片展示三餐详情

每个三餐卡片内部,左侧是餐次标签(早餐/午餐/晚餐/加餐),右侧是食物列表,信息密度适中,一目了然。

6.4 空状态与加载状态

  • 空状态:应用启动时显示欢迎语,引导用户输入
  • 加载状态:显示"🥗 正在定制餐单……"的提示
  • 结果状态:展示完整的营养餐单

三种状态的平滑切换,让用户始终清楚当前处于哪个阶段。

七、总结

营养餐单规划应用充分展示了鸿蒙ArkTS框架在健康类应用开发中的优势。通过 Flex 弹性布局实现的自适应胶囊选择器、@State 深度响应式的数组管理、@Builder 模板化的选择器组件,我们构建了一个交互流畅、视觉舒适的营养规划工具。

在AI应用层面,应用基于科学营养学原理,实现了目标热量估算、宏量营养素分配和忌口避让三大核心功能,为用户提供了个性化的饮食建议。未来可以进一步扩展的方向包括:集成食物数据库进行精确热量计算、支持自定义餐单微调、生成购物清单、以及根据用户反馈动态调整餐单等。


技术栈:HarmonyOS ArkTS | 声明式UI | @State | @Builder | Flex | ForEach | Divider
应用类型:AI + 健康管理
适用场景:减脂餐单、增肌饮食、日常营养规划

Logo

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

更多推荐