Flutter框架跨平台鸿蒙开发——饮食热量查询APP的开发流程
本文介绍了使用Flutter框架开发跨平台鸿蒙饮食热量查询APP的全流程。主要内容包括:应用概述与目标用户分析、开发环境搭建(Flutter+鸿蒙)、三层架构设计(UI/服务/数据层)以及核心功能实现。重点展示了食物数据模型设计、业务服务层实现和UI页面开发,通过代码示例详细说明了食物列表展示、搜索筛选和添加功能的关键实现方法。该应用可帮助用户查询食物营养成分,适用于健康饮食管理场景,充分体现了F
🚀运行效果展示



Flutter框架跨平台鸿蒙开发——饮食热量查询APP的开发流程
📝 前言
随着移动应用开发的快速发展,跨平台开发框架越来越受到开发者的青睐。Flutter作为Google推出的开源UI框架,凭借其"Write Once, Run Anywhere"的理念,以及优秀的性能和丰富的生态,成为了跨平台开发的热门选择。
华为鸿蒙系统(HarmonyOS)作为一款面向全场景的分布式操作系统,具有强大的设备互联能力和统一的开发框架。将Flutter与鸿蒙系统结合,既可以利用Flutter的跨平台优势,又能充分发挥鸿蒙系统的特性,为开发者和用户带来更好的体验。
本文将详细介绍如何使用Flutter框架开发一款跨平台的饮食热量查询APP,并成功在鸿蒙系统上运行。我们将从项目架构设计、核心功能实现、跨平台适配等方面,全面展示开发流程和技术要点。
🥗 应用介绍
应用概述
饮食热量查询APP是一款帮助用户查询各种食物热量和营养成分的工具型应用。用户可以通过搜索或分类浏览的方式,快速找到所需食物的详细营养信息,包括热量、蛋白质、碳水化合物和脂肪等。
目标用户
- 关注健康饮食的人群
- 正在减肥或健身的用户
- 需要控制饮食的慢性病患者
- 营养搭配师和健身教练
核心功能
- 食物列表展示:以卡片形式展示食物列表,包含名称、热量和分类信息
- 搜索功能:支持按食物名称搜索
- 分类筛选:支持按食物分类筛选
- 食物详情:展示食物的详细营养成分
- 添加食物:允许用户手动添加新的食物
🛠️ 开发环境搭建
1. Flutter环境搭建
- 安装Flutter SDK:从Flutter官网下载并安装最新版本的Flutter SDK
- 配置环境变量:将Flutter SDK的bin目录添加到系统环境变量中
- 验证安装:运行
flutter doctor命令,确保所有依赖项都已正确安装
2. 鸿蒙开发环境配置
- 安装DevEco Studio:从华为开发者官网下载并安装DevEco Studio
- 配置鸿蒙SDK:在DevEco Studio中下载并配置鸿蒙SDK
- 安装Flutter插件:在DevEco Studio中安装Flutter插件,支持Flutter开发
3. 项目创建
使用Flutter命令行工具创建项目:
flutter create calorie_tracker
🏗️ 项目架构设计
分层架构
本项目采用经典的三层架构设计,确保代码的可维护性和可扩展性:
模块划分
项目按照功能模块进行划分,主要包含以下模块:
lib/
├── calorie_tracker/
│ ├── models/ # 数据模型
│ ├── services/ # 业务逻辑
│ ├── screens/ # 页面组件
│ └── utils/ # 工具类
├── screens/ # 主页面
└── main.dart # 应用入口
🔧 核心功能实现
1. 数据模型设计
Food模型:定义食物的数据结构
/// 食物数据模型
class Food {
/// 食物ID
final int? id;
/// 食物名称
final String name;
/// 热量 (每100克/毫升)
final double calories;
/// 蛋白质 (每100克/毫升)
final double protein;
/// 碳水化合物 (每100克/毫升)
final double carbs;
/// 脂肪 (每100克/毫升)
final double fat;
/// 食物类型
final String category;
/// 构造函数
Food({
this.id,
required this.name,
required this.calories,
required this.protein,
required this.carbs,
required this.fat,
required this.category,
});
/// 从Map转换为Food对象
factory Food.fromMap(Map<String, dynamic> map) {
return Food(
id: map['id'],
name: map['name'],
calories: map['calories'],
protein: map['protein'],
carbs: map['carbs'],
fat: map['fat'],
category: map['category'],
);
}
/// 转换为Map对象
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'calories': calories,
'protein': protein,
'carbs': carbs,
'fat': fat,
'category': category,
};
}
}
2. 服务层实现
FoodService:处理食物数据的业务逻辑
/// 食物服务类,处理食物数据的业务逻辑
class FoodService {
/// 内存中的食物列表
final List<Food> _foods = [
Food(
id: 1,
name: '苹果',
calories: 52,
protein: 0.3,
carbs: 13.8,
fat: 0.2,
category: '水果',
),
// 其他食物数据...
];
/// 获取所有食物
Future<List<Food>> getAllFoods() async {
return _foods;
}
/// 根据名称搜索食物
Future<List<Food>> searchFoods(String keyword) async {
return _foods
.where((food) => food.name
.toLowerCase()
.contains(keyword.toLowerCase()))
.toList();
}
/// 根据分类获取食物
Future<List<Food>> getFoodsByCategory(String category) async {
return _foods.where((food) => food.category == category).toList();
}
/// 添加新食物
Future<int> addFood(Food food) async {
final newId = _foods.length + 1;
final newFood = Food(
id: newId,
name: food.name,
calories: food.calories,
protein: food.protein,
carbs: food.carbs,
fat: food.fat,
category: food.category,
);
_foods.add(newFood);
return newId;
}
/// 获取所有食物分类
Future<List<String>> getAllCategories() async {
return _foods.map((food) => food.category).toSet().toList();
}
}
3. UI层实现
食物列表页面
/// 食物列表页面
class FoodListScreen extends StatefulWidget {
/// 构造函数
const FoodListScreen({Key? key}) : super(key: key);
State<FoodListScreen> createState() => _FoodListScreenState();
}
class _FoodListScreenState extends State<FoodListScreen> {
/// 食物服务实例
final FoodService _foodService = FoodService();
/// 食物列表
List<Food> _foods = [];
/// 筛选后的食物列表
List<Food> _filteredFoods = [];
/// 所有分类
List<String> _categories = [];
/// 搜索关键词
String _searchKeyword = '';
/// 当前选中的分类
String? _selectedCategory;
/// 是否加载中
bool _isLoading = true;
void initState() {
super.initState();
_loadData();
}
/// 加载数据
Future<void> _loadData() async {
try {
setState(() {
_isLoading = true;
});
// 加载食物和分类
final foods = await _foodService.getAllFoods();
final categories = await _foodService.getAllCategories();
setState(() {
_foods = foods;
_filteredFoods = foods;
_categories = categories;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
_showError('加载数据失败');
}
}
// 其他方法...
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('饮食热量查询'),
backgroundColor: Colors.blueAccent,
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: Column(
children: [
// 搜索栏
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
onChanged: _searchFoods,
decoration: InputDecoration(
hintText: '搜索食物...',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0),
),
),
),
),
// 分类筛选
SizedBox(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _categories.length + 1,
itemBuilder: (context, index) {
final String category = index == 0
? '全部'
: _categories[index - 1];
final bool isSelected = _selectedCategory == category ||
(index == 0 && _selectedCategory == null);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: ChoiceChip(
label: Text(category),
selected: isSelected,
onSelected: (selected) {
if (selected) {
_selectCategory(index == 0 ? null : category);
}
},
selectedColor: Colors.blueAccent,
labelStyle: TextStyle(
color: isSelected ? Colors.white : Colors.black,
),
),
);
},
),
),
// 食物列表
Expanded(
child: ListView.builder(
itemCount: _filteredFoods.length,
itemBuilder: (context, index) {
final food = _filteredFoods[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(food.name),
subtitle: Text(
'${food.calories} 大卡/100g | ${food.category}',
),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () => _navigateToFoodDetail(food),
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _navigateToAddFood,
backgroundColor: Colors.blueAccent,
child: const Icon(Icons.add),
tooltip: '添加食物',
),
);
}
}
食物详情页面
/// 食物详情页面
class FoodDetailScreen extends StatelessWidget {
/// 食物对象
final Food food;
/// 构造函数
const FoodDetailScreen({Key? key, required this.food}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(food.name),
backgroundColor: Colors.blueAccent,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 食物名称和分类
Text(
food.name,
style: const TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8.0),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
decoration: BoxDecoration(
color: Colors.blueAccent.withOpacity(0.2),
borderRadius: BorderRadius.circular(16.0),
),
child: Text(
food.category,
style: TextStyle(
fontSize: 14.0,
color: Colors.blueAccent,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 24.0),
// 热量信息卡片
Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Text(
'热量信息',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${food.calories}',
style: const TextStyle(
fontSize: 48.0,
fontWeight: FontWeight.bold,
color: Colors.blueAccent,
),
),
const SizedBox(width: 8.0),
const Text(
'大卡/100g',
style: TextStyle(
fontSize: 16.0,
color: Colors.grey,
),
),
],
),
],
),
),
),
const SizedBox(height: 24.0),
// 营养成分信息
const Text(
'营养成分 (每100克)',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16.0),
// 蛋白质
_buildNutrientItem('蛋白质', food.protein, 'g', Colors.blue),
const SizedBox(height: 12.0),
// 碳水化合物
_buildNutrientItem('碳水化合物', food.carbs, 'g', Colors.green),
const SizedBox(height: 12.0),
// 脂肪
_buildNutrientItem('脂肪', food.fat, 'g', Colors.orange),
],
),
),
);
}
/// 构建营养成分项
Widget _buildNutrientItem(String name, double value, String unit, Color color) {
return Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
name,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
Row(
children: [
Text(
'$value',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: color,
),
),
const SizedBox(width: 4.0),
Text(
unit,
style: TextStyle(
fontSize: 14.0,
color: Colors.grey,
),
),
],
),
],
),
),
);
}
}
4. 导航设计
将饮食热量查询APP添加到主页面的功能卡片列表中:
// 饮食热量查询功能卡片
_buildFunctionCard(
context: context,
title: '饮食热量查询',
description: '查询各种食物的热量和营养成分',
icon: Icons.food_bank,
color: Colors.green,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const FoodListScreen(),
),
);
},
),
🔄 跨平台适配
1. 鸿蒙系统特殊处理
在开发过程中,我们遇到了SQLite数据库在鸿蒙系统上的兼容性问题。为了解决这个问题,我们采用了内存数据列表作为临时解决方案,确保应用能够正常运行。
2. 响应式布局
应用采用了响应式布局设计,确保在不同尺寸的鸿蒙设备上都能有良好的显示效果:
// 使用Expanded和Flexible组件实现响应式布局
Expanded(
child: ListView.builder(
// 列表内容
),
),
// 使用MediaQuery获取屏幕尺寸
final screenWidth = MediaQuery.of(context).size.width;
final itemWidth = screenWidth / 2 - 20;
3. 主题适配
应用支持鸿蒙系统的主题切换,确保与系统主题保持一致:
// 使用Theme.of(context)获取当前主题
Theme.of(context).primaryColor,
Theme.of(context).textTheme.headline6,
🐛 测试与调试
1. 遇到的问题
- SQLite数据库兼容性问题:在鸿蒙系统上,SQLite数据库初始化失败,导致数据无法加载
- 调试服务连接问题:Flutter DevTools与鸿蒙设备的连接不稳定
- 布局溢出问题:在某些鸿蒙设备上,部分页面出现布局溢出
2. 解决方案
- SQLite数据库问题:将数据存储方式从SQLite数据库改为内存中的数据列表
- 调试服务问题:使用
flutter run --verbose命令获取详细日志,调整调试参数 - 布局溢出问题:使用
SingleChildScrollView和Expanded组件优化布局,确保内容能够自适应屏幕尺寸
📊 开发流程总结
开发流程图
技术栈总结
| 技术类别 | 技术栈 |
|---|---|
| 开发框架 | Flutter 3.6.2 |
| 编程语言 | Dart |
| 状态管理 | 原生StatefulWidget |
| 数据存储 | 内存数据列表 |
| UI组件 | Material Design |
| 目标平台 | HarmonyOS |
🎯 总结与展望
总结
本文详细介绍了使用Flutter框架开发跨平台鸿蒙应用的完整流程,以饮食热量查询APP为例,展示了从项目初始化、架构设计到核心功能实现的全过程。我们遇到了SQLite数据库兼容性、调试服务连接等问题,并通过相应的解决方案成功解决。
通过本次开发实践,我们验证了Flutter框架在鸿蒙系统上的可行性,同时也积累了跨平台开发的经验。Flutter的跨平台优势和鸿蒙系统的特性相结合,为移动应用开发提供了新的思路和可能性。
展望
- 数据持久化优化:进一步研究SQLite数据库在鸿蒙系统上的兼容性问题,实现真正的数据持久化
- 功能扩展:添加更多功能,如饮食记录、热量统计、营养分析等
- 性能优化:优化应用性能,减少内存占用和启动时间
- 多端适配:进一步优化在不同鸿蒙设备上的显示效果
- 鸿蒙特性集成:集成鸿蒙系统的分布式能力,实现多设备协同
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)