🚀运行效果展示



Flutter框架跨平台鸿蒙开发------生日礼物推荐APP的开发流程
前言
在当今快节奏的生活中,为亲朋好友挑选合适的生日礼物常常成为一件令人头疼的事情。如何在众多选择中找到既符合对方喜好又有意义的礼物?基于这一需求,我们开发了一款生日礼物推荐APP,旨在帮助用户快速找到适合不同人群、不同预算的生日礼物灵感。
本项目采用Flutter框架进行开发,实现了跨平台鸿蒙系统的兼容,为用户提供了流畅、美观的使用体验。本文将详细介绍该APP的开发流程,包括核心功能实现、技术架构设计以及关键代码展示。
应用介绍
生日礼物推荐APP是一款专为用户提供生日礼物灵感的移动应用,主要功能包括:
- 礼物推荐:根据不同分类和预算推荐合适的生日礼物
- 分类筛选:支持按礼物类别进行筛选
- 搜索功能:快速查找特定类型的礼物
- 收藏功能:收藏心仪的礼物以便后续查看
- 礼物详情:查看礼物的详细信息,包括价格范围、适合人群等
应用界面设计简洁美观,采用响应式布局,确保在不同屏幕尺寸的设备上都能提供良好的用户体验。
技术架构
系统架构
本项目采用经典的三层架构设计:
- 数据层:负责数据的存储和管理,包括模型定义和模拟数据
- 服务层:负责业务逻辑处理,提供数据访问接口
- 表示层:负责用户界面展示和交互逻辑
技术栈
- 前端框架:Flutter 3.0+
- 开发语言:Dart
- 状态管理: setState (轻量级状态管理)
- 数据存储:内存存储(模拟数据)
- UI组件:Flutter内置组件
项目结构
lib/
├── models/ # 数据模型
│ └── birthday_gift_model.dart # 生日礼物模型
├── services/ # 服务层
│ └── birthday_gift_service.dart # 生日礼物服务
├── pages/ # 页面
│ ├── birthday_gift_home_page.dart # 主页
│ ├── birthday_gift_detail_page.dart # 详情页
│ └── birthday_gift_favorites_page.dart # 收藏页
├── utils/ # 工具类
│ └── mock_data.dart # 模拟数据
└── main.dart # 应用入口
核心功能实现
1. 数据模型设计
首先,我们定义了生日礼物的数据模型,包含礼物的基本信息:
dart
/// 生日礼物模型
/// 用于表示生日礼物的基本信息
class BirthdayGift {
/// 礼物ID
final String id;
/// 礼物名称
final String name;
/// 礼物描述
final String description;
/// 礼物分类
final String category;
/// 价格范围
final String priceRange;
/// 适合人群
final List<String> suitableFor;
/// 礼物图片(本地路径)
final String imagePath;
/// 是否收藏
bool isFavorite;
/// 构造函数
BirthdayGift({
required this.id,
required this.name,
required this.description,
required this.category,
required this.priceRange,
required this.suitableFor,
required this.imagePath,
this.isFavorite = false,
});
/// 从Map创建BirthdayGift实例
factory BirthdayGift.fromMap(Map<String, dynamic> map) {
return BirthdayGift(
id: map['id'],
name: map['name'],
description: map['description'],
category: map['category'],
priceRange: map['priceRange'],
suitableFor: List<String>.from(map['suitableFor']),
imagePath: map['imagePath'],
isFavorite: map['isFavorite'] ?? false,
);
}
/// 转换为Map
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'description': description,
'category': category,
'priceRange': priceRange,
'suitableFor': suitableFor,
'imagePath': imagePath,
'isFavorite': isFavorite,
};
}
}
2. 模拟数据实现
为了在开发阶段提供数据支持,我们创建了模拟数据:
dart
/// 生日礼物列表
static final List<BirthdayGift> birthdayGifts = [
BirthdayGift(
id: '1',
name: '个性化定制项链',
description: '可以刻字的银质项链,可定制对方的名字或特殊日期,简约而有意义。',
category: '饰品',
priceRange: '¥100-300',
suitableFor: ['女性', '朋友', '恋人'],
imagePath: 'images/R-C.jpg',
isFavorite: false,
),
BirthdayGift(
id: '2',
name: '智能手环',
description: '多功能智能手环,可监测心率、睡眠质量,支持运动模式,时尚实用。',
category: '电子产品',
priceRange: '¥200-500',
suitableFor: ['男性', '女性', '朋友', '家人'],
imagePath: 'images/u=174051936,621369760&fm=253&gp=0.jpg',
isFavorite: false,
),
// 更多礼物数据...
];
3. 服务层实现
服务层负责处理业务逻辑,提供数据访问接口:
dart
/// 生日礼物服务
/// 用于提供生日礼物数据和相关操作
class BirthdayGiftService {
/// 获取所有生日礼物
/// 返回生日礼物列表
List<BirthdayGift> getAllGifts() {
return MockData.birthdayGifts;
}
/// 根据分类获取礼物
/// [category] 礼物分类
/// 返回对应分类的礼物列表
List<BirthdayGift> getGiftsByCategory(String category) {
if (category == '全部') {
return getAllGifts();
}
return MockData.birthdayGifts.where((gift) => gift.category == category).toList();
}
/// 搜索礼物
/// [keyword] 搜索关键词
/// 返回包含关键词的礼物列表
List<BirthdayGift> searchGifts(String keyword) {
if (keyword.isEmpty) {
return getAllGifts();
}
return MockData.birthdayGifts.where((gift) =>
gift.name.toLowerCase().contains(keyword.toLowerCase()) ||
gift.description.toLowerCase().contains(keyword.toLowerCase()) ||
gift.category.toLowerCase().contains(keyword.toLowerCase())
).toList();
}
/// 切换礼物收藏状态
/// [giftId] 礼物ID
/// 返回更新后的收藏状态
bool toggleFavorite(String giftId) {
final gift = MockData.birthdayGifts.firstWhere((gift) => gift.id == giftId, orElse: () => BirthdayGift(
id: '',
name: '',
description: '',
category: '',
priceRange: '',
suitableFor: [],
imagePath: '',
));
if (gift.id.isNotEmpty) {
gift.isFavorite = !gift.isFavorite;
return gift.isFavorite;
}
return false;
}
/// 获取收藏的礼物
/// 返回收藏的礼物列表
List<BirthdayGift> getFavoriteGifts() {
return MockData.birthdayGifts.where((gift) => gift.isFavorite).toList();
}
}
4. 页面实现
4.1 主页实现
主页是应用的核心页面,包含礼物列表、分类筛选和搜索功能:
dart
class BirthdayGiftHomePage extends StatefulWidget {
/// 构造函数
const BirthdayGiftHomePage({super.key});
@override
State<BirthdayGiftHomePage> createState() => _BirthdayGiftHomePageState();
}
class _BirthdayGiftHomePageState extends State<BirthdayGiftHomePage> {
/// 礼物服务
final BirthdayGiftService _giftService = BirthdayGiftService();
/// 礼物列表
late List<BirthdayGift> _gifts;
/// 分类列表
late List<String> _categories;
/// 当前选中的分类
String _selectedCategory = '全部';
/// 搜索关键词
String _searchKeyword = '';
/// 搜索控制器
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
_loadGifts();
_loadCategories();
}
/// 加载礼物数据
void _loadGifts() {
setState(() {
_gifts = _giftService.getAllGifts();
});
}
/// 加载分类数据
void _loadCategories() {
setState(() {
_categories = _giftService.getAllCategories();
});
}
/// 根据分类筛选礼物
void _filterGiftsByCategory(String category) {
setState(() {
_selectedCategory = category;
if (category == '全部') {
_gifts = _giftService.getAllGifts();
} else {
_gifts = _giftService.getGiftsByCategory(category);
}
});
}
/// 搜索礼物
void _searchGifts() {
setState(() {
_gifts = _giftService.searchGifts(_searchKeyword);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('生日礼物推荐'),
actions: [
IconButton(
icon: const Icon(Icons.favorite),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BirthdayGiftFavoritesPage()),
);
},
),
],
),
body: Column(
children: [
// 搜索栏
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: '搜索礼物...',
prefixIcon: const Icon(Icons.search),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_searchController.clear();
_searchKeyword = '';
_loadGifts();
},
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
onSubmitted: (value) {
_searchKeyword = value;
_searchGifts();
},
),
),
// 分类选择
SizedBox(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _categories.length,
itemBuilder: (context, index) {
final category = _categories[index];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ElevatedButton(
onPressed: () => _filterGiftsByCategory(category),
style: ElevatedButton.styleFrom(
backgroundColor: _selectedCategory == category
? Theme.of(context).primaryColor
: Colors.grey[200],
foregroundColor: _selectedCategory == category
? Colors.white
: Colors.black,
),
child: Text(category),
),
);
},
),
),
// 礼物列表
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(16.0),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
childAspectRatio: 0.7,
),
itemCount: _gifts.length,
itemBuilder: (context, index) {
final gift = _gifts[index];
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BirthdayGiftDetailPage(giftId: gift.id),
),
);
},
child: Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 礼物图片
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12.0)),
child: Image.asset(
gift.imagePath,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
// 礼物信息
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
gift.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
IconButton(
icon: Icon(
gift.isFavorite ? Icons.favorite : Icons.favorite_border,
color: gift.isFavorite ? Colors.red : null,
),
onPressed: () => _toggleFavorite(gift.id),
),
],
),
Text(
gift.category,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12.0,
),
),
Text(
gift.priceRange,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12.0,
),
),
],
),
),
],
),
),
);
},
),
),
],
),
);
}
}
4.2 详情页实现
详情页展示礼物的详细信息,包括图片、描述、价格范围和适合人群:
dart
class BirthdayGiftDetailPage extends StatefulWidget {
/// 礼物ID
final String giftId;
/// 构造函数
const BirthdayGiftDetailPage({super.key, required this.giftId});
@override
State<BirthdayGiftDetailPage> createState() => _BirthdayGiftDetailPageState();
}
class _BirthdayGiftDetailPageState extends State<BirthdayGiftDetailPage> {
/// 礼物服务
final BirthdayGiftService _giftService = BirthdayGiftService();
/// 当前礼物
late BirthdayGift _gift;
/// 是否加载完成
bool _isLoaded = false;
@override
void initState() {
super.initState();
_loadGiftDetail();
}
/// 加载礼物详情
void _loadGiftDetail() {
final gift = _giftService.getGiftById(widget.giftId);
if (gift != null) {
setState(() {
_gift = gift;
_isLoaded = true;
});
}
}
/// 切换礼物收藏状态
void _toggleFavorite() {
setState(() {
_giftService.toggleFavorite(_gift.id);
_gift.isFavorite = !_gift.isFavorite;
});
}
@override
Widget build(BuildContext context) {
if (!_isLoaded) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('礼物详情'),
actions: [
IconButton(
icon: Icon(
_gift.isFavorite ? Icons.favorite : Icons.favorite_border,
color: _gift.isFavorite ? Colors.red : null,
),
onPressed: _toggleFavorite,
),
],
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 礼物图片
Container(
width: double.infinity,
height: 300,
child: Image.asset(
_gift.imagePath,
fit: BoxFit.cover,
),
),
// 礼物信息
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 礼物名称
Text(
_gift.name,
style: const TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8.0),
// 价格范围
Row(
children: [
const Icon(Icons.price_check, size: 16.0, color: Colors.grey),
const SizedBox(width: 4.0),
Text(
_gift.priceRange,
style: TextStyle(
fontSize: 16.0,
color: Colors.grey[600],
),
),
],
),
const SizedBox(height: 8.0),
// 分类
Row(
children: [
const Icon(Icons.category, size: 16.0, color: Colors.grey),
const SizedBox(width: 4.0),
Text(
_gift.category,
style: TextStyle(
fontSize: 16.0,
color: Colors.grey[600],
),
),
],
),
const SizedBox(height: 16.0),
// 适合人群
const Text(
'适合人群',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8.0),
Wrap(
spacing: 8.0,
runSpacing: 8.0,
children: _gift.suitableFor.map((person) {
return Chip(
label: Text(person),
backgroundColor: Colors.blue[100],
);
}).toList(),
),
const SizedBox(height: 16.0),
// 礼物描述
const Text(
'礼物描述',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8.0),
Text(
_gift.description,
style: const TextStyle(
fontSize: 16.0,
height: 1.5,
),
),
],
),
),
],
),
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.all(16.0),
child: ElevatedButton(
onPressed: () {
// 这里可以添加购买或查看更多信息的逻辑
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('感谢您的关注!')),
);
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16.0),
backgroundColor: Theme.of(context).primaryColor,
),
child: const Text(
'查看更多信息',
style: TextStyle(fontSize: 18.0),
),
),
),
);
}
}
4.3 收藏页实现
收藏页展示用户收藏的礼物列表:
dart
class BirthdayGiftFavoritesPage extends StatefulWidget {
/// 构造函数
const BirthdayGiftFavoritesPage({super.key});
@override
State<BirthdayGiftFavoritesPage> createState() => _BirthdayGiftFavoritesPageState();
}
class _BirthdayGiftFavoritesPageState extends State<BirthdayGiftFavoritesPage> {
/// 礼物服务
final BirthdayGiftService _giftService = BirthdayGiftService();
/// 收藏的礼物列表
late List<BirthdayGift> _favoriteGifts;
@override
void initState() {
super.initState();
_loadFavoriteGifts();
}
/// 加载收藏的礼物
void _loadFavoriteGifts() {
setState(() {
_favoriteGifts = _giftService.getFavoriteGifts();
});
}
/// 切换礼物收藏状态
void _toggleFavorite(String giftId) {
setState(() {
_giftService.toggleFavorite(giftId);
// 重新加载收藏的礼物
_favoriteGifts = _giftService.getFavoriteGifts();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的收藏'),
),
body: _favoriteGifts.isEmpty
? const Center(
child: Text('还没有收藏的礼物'),
)
: GridView.builder(
padding: const EdgeInsets.all(16.0),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
childAspectRatio: 0.7,
),
itemCount: _favoriteGifts.length,
itemBuilder: (context, index) {
final gift = _favoriteGifts[index];
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BirthdayGiftDetailPage(giftId: gift.id),
),
).then((_) {
// 从详情页面返回后重新加载收藏列表
_loadFavoriteGifts();
});
},
child: Card(
elevation: 4.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 礼物图片
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(12.0)),
child: Image.asset(
gift.imagePath,
width: double.infinity,
fit: BoxFit.cover,
),
),
),
// 礼物信息
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
gift.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
IconButton(
icon: Icon(
Icons.favorite,
color: Colors.red,
),
onPressed: () => _toggleFavorite(gift.id),
),
],
),
Text(
gift.category,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12.0,
),
),
Text(
gift.priceRange,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12.0,
),
),
],
),
),
],
),
),
);
},
),
);
}
}
5. 应用入口配置
在main.dart文件中配置应用路由:
dart
/// 应用入口文件
/// 配置应用路由和主题
import 'package:flutter/material.dart';
import 'pages/birthday_gift_home_page.dart';
import 'pages/birthday_gift_detail_page.dart';
import 'pages/birthday_gift_favorites_page.dart';
void main() {
runApp(const MyApp());
}
/// 应用主类
class MyApp extends StatelessWidget {
/// 构造函数
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '生日礼物推荐',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const BirthdayGiftHomePage(),
routes: {
'/birthday_gift_detail': (context) => BirthdayGiftDetailPage(
giftId: ModalRoute.of(context)?.settings.arguments as String,
),
'/birthday_gift_favorites': (context) => const BirthdayGiftFavoritesPage(),
},
debugShowCheckedModeBanner: false,
);
}
}
开发流程
1. 需求分析与规划
- 需求分析:明确应用的核心功能和目标用户
- 技术选型:选择Flutter框架进行跨平台开发
- 架构设计:设计三层架构,确定项目结构
2. 数据模型设计
- 定义模型:创建BirthdayGift模型,包含礼物的基本信息
- 模拟数据:创建模拟数据,用于开发和测试
3. 服务层实现
- 业务逻辑:实现礼物的增删改查、分类筛选和搜索功能
- 数据访问:提供统一的数据访问接口
4. 页面开发
- 主页:实现礼物列表展示、分类筛选和搜索功能
- 详情页:实现礼物详细信息展示和收藏功能
- 收藏页:实现收藏礼物的管理
5. 测试与优化
- 功能测试:测试应用的各项功能是否正常
- 性能优化:优化应用的加载速度和响应时间
- UI优化:改进用户界面,提升用户体验
6. 构建与发布
- 构建应用:构建适合鸿蒙系统的应用包
- 测试部署:在鸿蒙设备上测试应用
- 发布应用:准备应用发布所需的材料
流程图
应用架构流程
用户界面
服务层
数据层
礼物浏览流程
启动应用
进入主页
浏览礼物列表
选择分类筛选
搜索礼物
点击礼物查看详情
查看礼物详情
收藏礼物
返回主页
进入收藏页
查看收藏的礼物
技术难点与解决方案
1. 响应式布局设计
难点:确保应用在不同屏幕尺寸的设备上都能提供良好的用户体验。
解决方案:
- 使用Flutter的MediaQuery获取屏幕尺寸
- 采用Flex布局和GridView等响应式组件
- 避免使用固定宽度和高度,使用相对尺寸
2. 状态管理
难点:管理应用中的状态,特别是收藏状态的同步。
解决方案:
- 使用setState进行轻量级状态管理
- 在状态变化时及时更新UI
- 通过服务层统一管理状态,确保数据一致性
3. 数据模拟
难点:在没有后端API的情况下提供真实的数据体验。
解决方案:
- 创建结构化的模拟数据
- 实现完整的服务层接口,模拟真实API行为
- 确保模拟数据的多样性和真实性
📚 参考资料
总结
通过本项目的开发,我们成功实现了一款功能完整、界面美观的生日礼物推荐APP,主要成果包括:
- 跨平台兼容:使用Flutter框架实现了跨平台鸿蒙系统的兼容
- 核心功能:实现了礼物推荐、分类筛选、搜索、收藏等核心功能
- 用户体验:采用响应式布局,提供流畅、直观的用户体验
- 技术架构:采用三层架构设计,代码结构清晰,易于维护
本项目展示了Flutter框架在跨平台移动应用开发中的优势,特别是其丰富的UI组件和简洁的开发方式。同时,我们也积累了鸿蒙系统应用开发的经验,为后续的跨平台开发项目打下了基础。
未来,我们可以考虑以下方向的改进:
- 后端集成:接入真实的后端API,提供更丰富的礼物数据
- 个性化推荐:基于用户的浏览和收藏历史,提供个性化的礼物推荐
- 社交分享:添加礼物分享功能,允许用户将心仪的礼物分享给朋友
- AR预览:利用AR技术,让用户可以预览礼物的实际效果
生日礼物推荐APP的开发过程是一次愉快的技术探索之旅,我们不仅实现了应用的功能需求,也提升了团队的技术能力。希望这款应用能够为用户带来实实在在的帮助,让生日礼物的选择变得更加轻松愉快。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net