1. 项目概述:Flutter for OpenHarmony 美食烹饪助手 App
作为一名有多年移动开发经验的工程师,我最近在开发一款基于Flutter和OpenHarmony的美食烹饪助手应用。这个项目的核心目标是打造一个跨平台的菜谱管理工具,让用户能够方便地收藏、创建和浏览各类美食菜谱。
菜谱库模块是整个应用的核心功能区域,相当于应用的"心脏"。它需要整合用户最常用的功能入口,同时提供直观的内容展示。在本文中,我将详细介绍如何实现这个关键模块的主界面,分享我在设计和技术实现上的思考过程。
2. 核心需求解析与设计思路
2.1 功能需求拆解
菜谱库主界面需要承载以下核心功能:
- 展示用户收藏的菜谱数量统计
- 提供快速访问常用功能的入口(收藏、历史、创建、搜索)
- 显示用户最近浏览或创建的菜谱列表
- 支持快速跳转到各个子功能页面
这些功能看似简单,但要在一个页面上合理组织,需要仔细考虑用户体验和界面布局。经过多次原型设计,我最终确定了分区布局的方案。
2.2 界面布局设计
我采用了三区布局结构:
- 顶部统计卡片:使用渐变背景增强视觉吸引力,展示关键数据
- 中部快速入口:四个功能按钮平均分布,采用统一的视觉设计
- 底部菜谱列表:横向滚动的卡片式布局,展示最近菜谱
这种布局的优势在于:
- 层次分明,用户能快速理解页面结构
- 功能分区明确,避免视觉混乱
- 符合移动端用户从上到下的浏览习惯
- 充分利用屏幕空间,信息密度适中
2.3 技术选型考量
在技术实现上,我做了以下关键选择:
- 使用StatelessWidget:因为主界面内容相对固定,不需要频繁更新状态
- 采用GetX路由管理:简化页面跳转逻辑,代码更清晰
- ScreenUtil适配:确保在不同尺寸设备上都能良好显示
- 数据驱动UI:快速入口使用Map列表定义,便于维护和扩展
这些选择基于我在多个Flutter项目中的实践经验,能够在保证性能的同时提高开发效率。
3. 详细实现过程
3.1 基础页面结构搭建
首先创建基本的页面骨架:
dart复制import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class RecipeLibraryPage extends StatelessWidget {
const RecipeLibraryPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('菜谱库'),
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () => Get.to(() => const RecipeSearchPage()),
),
],
),
body: SingleChildScrollView(
child: Column(
children: [
_buildHeader(),
SizedBox(height: 20.h),
_buildQuickActions(),
SizedBox(height: 20.h),
_buildMyRecipes(),
SizedBox(height: 20.h),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Get.to(() => const CreateRecipePage()),
backgroundColor: Colors.orange,
child: const Icon(Icons.add),
),
);
}
}
关键点说明:
- 使用Scaffold作为基础布局框架
- AppBar右侧添加搜索按钮,符合用户习惯
- 主体内容使用SingleChildScrollView包裹,确保可滚动
- 浮动按钮用于快速创建菜谱,采用主题色
3.2 头部统计卡片实现
头部卡片是页面的视觉焦点,需要精心设计:
dart复制Widget _buildHeader() {
return Container(
margin: EdgeInsets.all(16.w),
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.orange.shade400, Colors.orange.shade600],
),
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'我的菜谱库',
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 8.h),
Text(
'已收藏 50 道菜谱',
style: TextStyle(color: Colors.white, fontSize: 14.sp),
),
],
),
),
Icon(Icons.book, size: 60.sp, color: Colors.white.withOpacity(0.5)),
],
),
);
}
设计要点:
- 使用橙色渐变背景增强视觉冲击力
- 文字信息左对齐,图标右对齐,布局平衡
- 主标题使用20sp粗体,副标题14sp,层次分明
- 图标采用半透明效果,避免喧宾夺主
- 圆角半径与整体设计语言保持一致
3.3 快速入口区域实现
快速入口区域采用图标按钮形式:
dart复制Widget _buildQuickActions() {
final actions = [
{'icon': Icons.favorite, 'title': '我的收藏', 'page': const FavoriteRecipesPage()},
{'icon': Icons.history, 'title': '浏览历史', 'page': const BrowseHistoryPage()},
{'icon': Icons.create, 'title': '创建菜谱', 'page': const CreateRecipePage()},
{'icon': Icons.search, 'title': '搜索菜谱', 'page': const RecipeSearchPage()},
];
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: actions.map((action) {
return GestureDetector(
onTap: () => Get.to(() => action['page'] as Widget),
child: Column(
children: [
Container(
width: 60.w,
height: 60.w,
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(
action['icon'] as IconData,
color: Colors.orange,
size: 30.sp,
),
),
SizedBox(height: 8.h),
Text(
action['title'] as String,
style: TextStyle(fontSize: 12.sp),
),
],
),
);
}).toList(),
),
);
}
实现细节:
- 使用Map列表定义所有入口,便于维护
- 每个入口包含60x60的圆角容器和30sp图标
- 图标背景使用浅橙色,与主题呼应
- 标签文字12sp,确保可读性
- 使用spaceAround平均分布按钮
- GestureDetector实现点击交互
3.4 菜谱列表实现
菜谱列表采用横向滚动布局:
dart复制Widget _buildMyRecipes() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'我的菜谱',
style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
),
TextButton(
onPressed: () => Get.to(() => const MyRecipesPage()),
child: const Text('查看全部'),
),
],
),
),
SizedBox(height: 12.h),
SizedBox(
height: 200.h,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: 10,
itemBuilder: (context, index) {
return _buildRecipeCard(index);
},
),
),
],
);
}
关键设计:
- 标题和"查看全部"按钮水平排列
- 列表高度固定为200h,确保一致性
- 横向滚动,符合移动端操作习惯
- 左右padding保证内容不贴边
3.5 菜谱卡片组件实现
单个菜谱卡片的实现:
dart复制Widget _buildRecipeCard(int index) {
return Container(
width: 150.w,
margin: EdgeInsets.only(right: 12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 5,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 120.h,
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.vertical(top: Radius.circular(12.r)),
),
child: Center(
child: Icon(Icons.restaurant, size: 50.sp, color: Colors.orange),
),
),
Padding(
padding: EdgeInsets.all(8.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'我的菜谱 ${index + 1}',
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4.h),
Row(
children: [
Icon(Icons.timer, size: 12.sp, color: Colors.grey),
SizedBox(width: 4.w),
Text('30分钟', style: TextStyle(fontSize: 11.sp, color: Colors.grey)),
],
),
],
),
),
],
),
);
}
卡片设计要点:
- 固定宽度150w,确保横向滚动效果
- 图片区域高度120h,文字区域高度自适应
- 顶部圆角与卡片圆角对齐
- 文字信息包含名称和烹饪时间
- 名称过长时显示省略号
- 使用轻微阴影增强层次感
4. 高级功能与优化
4.1 下拉刷新实现
为提升用户体验,添加下拉刷新功能:
dart复制body: RefreshIndicator(
onRefresh: () async {
// 实际项目中这里应该调用API刷新数据
await Future.delayed(const Duration(seconds: 1));
// 可以在这里添加数据更新逻辑
},
child: SingleChildScrollView(
child: Column(
// ...原有内容
),
),
),
实现说明:
- RefreshIndicator包裹原有内容
- onRefresh回调中执行数据刷新
- 使用Future.delayed模拟网络请求
- 实际项目中应替换为真实API调用
4.2 骨架屏加载效果
为改善加载体验,添加骨架屏:
dart复制bool isLoading = true;
// 在build方法中添加加载状态判断
if (isLoading) {
return _buildSkeleton();
}
// 骨架屏实现
Widget _buildSkeleton() {
return Column(
children: [
Container(
margin: EdgeInsets.all(16.w),
height: 100.h,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(12.r),
),
),
SizedBox(height: 20.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(4, (index) =>
Container(
width: 60.w,
height: 80.h,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(12.r),
),
),
),
),
// 更多骨架屏元素...
],
);
}
骨架屏设计原则:
- 模拟真实内容的布局结构
- 使用灰色占位块表示加载中的内容
- 保持与实际UI相同的间距和尺寸
- 在数据加载完成后切换为真实内容
4.3 响应式设计优化
为确保在不同设备上都能良好显示:
- 使用ScreenUtil进行尺寸适配:
dart复制// 在main.dart中初始化
void main() {
runApp(ScreenUtilInit(
designSize: const Size(375, 812), // 设计稿尺寸
builder: (_, child) => MyApp(),
));
}
- 所有尺寸使用.w/.h/.sp后缀:
dart复制// 示例
SizedBox(height: 20.h),
Text('标题', style: TextStyle(fontSize: 16.sp)),
Container(width: 100.w, height: 100.h),
- 针对平板设备特殊适配:
dart复制// 根据屏幕宽度调整布局
if (MediaQuery.of(context).size.width > 600) {
// 平板布局
} else {
// 手机布局
}
5. 开发经验与注意事项
5.1 性能优化技巧
-
列表性能优化:
- 对于长列表,使用ListView.builder而非直接使用Column
- 设置itemExtent提高滚动性能
- 考虑使用flutter_staggered_grid_view等第三方库处理复杂布局
-
图片加载优化:
- 使用cached_network_image缓存网络图片
- 为占位图设置合适的尺寸,避免布局跳动
- 考虑使用WebP格式减小图片体积
-
状态管理选择:
- 简单页面使用StatelessWidget
- 需要状态管理的页面考虑使用GetX或Provider
- 避免在build方法中执行耗时操作
5.2 常见问题与解决方案
-
UI渲染问题:
- 问题:某些设备上布局错乱
- 解决:确保所有尺寸使用ScreenUtil适配
- 检查父容件的约束条件
-
路由跳转卡顿:
- 问题:页面跳转时有明显卡顿
- 解决:使用GetX的off/offAll方法替代to
- 考虑预加载可能跳转的页面
-
内存泄漏:
- 问题:页面多次打开后内存占用持续增加
- 解决:检查控制器是否及时dispose
- 使用DevTools的内存分析工具定位问题
5.3 设计系统一致性
-
颜色系统:
- 定义主题色和辅助色
- 使用MaterialColor生成不同深浅的颜色
- 保持整个应用颜色风格一致
-
间距系统:
- 定义统一的间距标准(如8、16、24等)
- 使用const常量管理常用间距
- 确保相同功能的间距保持一致
-
字体系统:
- 定义标题、正文、辅助文字等字体样式
- 使用TextTheme统一管理
- 确保不同设备上文字可读性
6. 项目扩展与未来计划
6.1 功能扩展方向
-
云端同步:
- 实现用户数据跨设备同步
- 考虑使用Firebase或自建后端服务
- 添加离线模式支持
-
智能推荐:
- 基于用户历史行为推荐菜谱
- 实现食材匹配功能
- 添加季节性推荐
-
社交功能:
- 允许用户分享菜谱
- 添加评论和评分系统
- 实现用户关注功能
6.2 技术优化计划
-
状态管理升级:
- 引入更复杂的状态管理方案
- 考虑使用Riverpod或Bloc
- 实现更细粒度的状态更新
-
动画效果增强:
- 添加页面切换动画
- 实现交互式微动画
- 使用Rive实现复杂动画
-
测试覆盖:
- 增加单元测试和Widget测试
- 实现集成测试流程
- 添加性能测试监控
6.3 跨平台适配计划
-
OpenHarmony深度适配:
- 利用OpenHarmony特有功能
- 优化性能表现
- 确保UI一致性
-
Web版开发:
- 使用相同的代码库构建Web版
- 适配桌面端交互方式
- 优化加载性能
-
桌面端支持:
- 开发Windows/macOS版本
- 添加桌面端特有功能
- 优化大屏幕体验
在实际开发过程中,我发现Flutter for OpenHarmony的生态还在快速发展中,某些功能可能需要自定义实现。建议保持关注官方文档更新,及时调整实现方案。对于性能敏感的部分,可以考虑编写平台特定的原生代码来优化。