Files
kitchen/lib/src/pages/profile/profile_home.dart
Developer ceb11d9aac feat: 新增口味偏好服务和菜谱分享功能
- 新增 TastePreferenceService 用于管理用户口味偏好设置
- 实现菜谱分享功能,包括 RecipeShareService 和分享页面
- 更新平台工具类以支持鸿蒙系统检测
- 优化收藏页和农场商店页面的UI交互
- 添加新的参考文献和关于页面内容
- 更新API文档至v3.3.0版本
2026-04-18 08:29:31 +08:00

489 lines
15 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 文件: profile_home.dart
* 名称: 个人中心首页标签
* 作用: iOS 26 风格的用户信息展示、功能入口和消息预览
* 更新: 2026-04-09 重构为 Liquid Glass 风格,统一使用 DesignTokens
* 更新: 2026-04-12 添加滚动物理特性,支持上下滑动
*/
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:mom_kitchen/src/config/design_tokens.dart';
import 'package:mom_kitchen/src/controllers/user/profile_controller.dart';
import 'package:mom_kitchen/src/pages/profile/settings/personalization_page.dart';
import 'package:mom_kitchen/src/config/app_routes.dart';
class ProfileHomeTab extends StatelessWidget {
const ProfileHomeTab({super.key});
@override
Widget build(BuildContext context) {
final profileController = Get.find<ProfileController>();
final isDark = CupertinoTheme.brightnessOf(context) == Brightness.dark;
return Obx(
() => SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space4,
vertical: DesignTokens.space2,
),
child: Column(
children: [
_buildUserCard(profileController, isDark),
const SizedBox(height: DesignTokens.space4),
_buildFeatureGrid(isDark),
const SizedBox(height: DesignTokens.space4),
_buildToolsGrid(isDark),
const SizedBox(height: DesignTokens.space4),
_buildMessagePreview(isDark),
const SizedBox(height: DesignTokens.space5),
_buildAppFooter(isDark),
const SizedBox(height: DesignTokens.space6),
],
),
),
);
}
Widget _buildToolsGrid(bool isDark) {
final tools = [
_FeatureItem(
CupertinoIcons.timer,
'烹饪计时',
DesignTokens.orange,
AppRoutes.cookingTimer,
),
_FeatureItem(
CupertinoIcons.arrow_2_circlepath,
'用量换算',
DesignTokens.secondary,
AppRoutes.unitConverter,
),
_FeatureItem(
CupertinoIcons.chart_bar,
'BMI计算',
DesignTokens.green,
AppRoutes.bmiCalculator,
),
_FeatureItem(
CupertinoIcons.arrow_up_arrow_down,
'份量缩放',
DesignTokens.dynamicPrimary,
AppRoutes.servingScaler,
),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space1,
vertical: DesignTokens.space2,
),
child: Text(
'🛠️ 实用工具',
style: TextStyle(
fontSize: DesignTokens.fontMd,
fontWeight: FontWeight.w600,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
),
const SizedBox(height: DesignTokens.space2),
SizedBox(
height: 100,
child: ListView.separated(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space3,
),
itemCount: tools.length,
separatorBuilder: (_, _) =>
const SizedBox(width: DesignTokens.space3),
itemBuilder: (context, index) {
final tool = tools[index];
return GestureDetector(
onTap: () {
if (tool.route != null) {
Get.toNamed(tool.route!);
}
},
behavior: HitTestBehavior.opaque,
child: Container(
width: 80,
decoration: BoxDecoration(
color: isDark ? DarkDesignTokens.card : DesignTokens.card,
borderRadius: DesignTokens.borderRadiusLg,
boxShadow: DesignTokens.shadowsSm,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: tool.color.withValues(alpha: 0.12),
borderRadius: DesignTokens.borderRadiusMd,
),
child: Icon(tool.icon, size: 24, color: tool.color),
),
const SizedBox(height: DesignTokens.space2),
Text(
tool.label,
style: TextStyle(
fontSize: DesignTokens.fontXs,
color: isDark
? DarkDesignTokens.text2
: DesignTokens.text2,
),
textAlign: TextAlign.center,
),
],
),
),
);
},
),
),
],
);
}
Widget _buildUserCard(ProfileController controller, bool isDark) {
final user = controller.user.value;
final isLoggedIn = controller.isLoggedIn.value;
return Container(
padding: const EdgeInsets.all(DesignTokens.space4),
decoration: BoxDecoration(
color: isDark ? DarkDesignTokens.card : DesignTokens.card,
borderRadius: DesignTokens.borderRadiusLg,
boxShadow: DesignTokens.shadowsSm,
),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: DesignTokens.primaryLight,
),
child: isLoggedIn && user != null && user.avatar != null
? ClipOval(
child: Image.network(
user.avatar!,
width: 56,
height: 56,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Icon(
CupertinoIcons.person_fill,
size: 28,
color: DesignTokens.dynamicPrimary,
);
},
),
)
: Icon(
CupertinoIcons.person_fill,
size: 28,
color: DesignTokens.dynamicPrimary,
),
),
const SizedBox(width: DesignTokens.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isLoggedIn && user != null && user.isNotEmpty
? user.name
: '未登录',
style: TextStyle(
fontSize: DesignTokens.fontLg,
fontWeight: FontWeight.w700,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
const SizedBox(height: DesignTokens.space1),
Text(
isLoggedIn && user != null && user.isNotEmpty
? user.email
: '点击登录',
style: TextStyle(
fontSize: DesignTokens.fontSm,
color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3,
),
),
],
),
),
const SizedBox(width: DesignTokens.space2),
_buildMiniButton(
isLoggedIn ? '编辑' : '登录',
isDark,
isLoggedIn
? null
: () => controller.login('test@example.com', '123456'),
),
const SizedBox(width: DesignTokens.space2),
_buildMiniButton(
'个性化',
isDark,
() => Get.to(() => const PersonalizationPage()),
),
],
),
);
}
Widget _buildMiniButton(String text, bool isDark, VoidCallback? onPressed) {
return CupertinoButton(
padding: EdgeInsets.symmetric(
horizontal: DesignTokens.space3,
vertical: DesignTokens.space1,
),
minimumSize: Size.zero,
borderRadius: DesignTokens.borderRadiusSm,
color: DesignTokens.dynamicPrimary,
onPressed: onPressed,
child: Text(
text,
style: const TextStyle(
fontSize: DesignTokens.fontXs,
fontWeight: FontWeight.w500,
color: CupertinoColors.white,
),
),
);
}
Widget _buildFeatureGrid(bool isDark) {
final items = [
_FeatureItem(
CupertinoIcons.star,
'技巧',
DesignTokens.orange,
AppRoutes.cookingTips,
),
_FeatureItem(
CupertinoIcons.heart,
'迷你卡片',
DesignTokens.red,
AppRoutes.miniCard,
),
_FeatureItem(
CupertinoIcons.cart,
'分享记录',
DesignTokens.green,
AppRoutes.shoppingList,
),
_FeatureItem(
CupertinoIcons.bookmark,
'评分记录',
DesignTokens.secondary,
AppRoutes.favorites,
),
];
return Container(
padding: const EdgeInsets.all(DesignTokens.space3),
decoration: BoxDecoration(
color: isDark ? DarkDesignTokens.card : DesignTokens.card,
borderRadius: DesignTokens.borderRadiusLg,
boxShadow: DesignTokens.shadowsSm,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items.map((item) {
return GestureDetector(
onTap: () {
if (item.route != null) {
Get.toNamed(item.route!);
}
},
behavior: HitTestBehavior.opaque,
child: SizedBox(
width: 64,
child: Column(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: item.color.withValues(alpha: 0.12),
borderRadius: DesignTokens.borderRadiusMd,
),
child: Icon(item.icon, size: 20, color: item.color),
),
const SizedBox(height: DesignTokens.space2),
Text(
item.label,
style: TextStyle(
fontSize: DesignTokens.fontXs,
color: isDark
? DarkDesignTokens.text2
: DesignTokens.text2,
),
),
],
),
),
);
}).toList(),
),
);
}
Widget _buildMessagePreview(bool isDark) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: DesignTokens.space1,
vertical: DesignTokens.space2,
),
child: Text(
'最新消息',
style: TextStyle(
fontSize: DesignTokens.fontMd,
fontWeight: FontWeight.w600,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
),
const SizedBox(height: DesignTokens.space2),
GestureDetector(
onTap: () => Get.toNamed(AppRoutes.dataCenter),
child: _buildMessageItem('📊 数据管理中心', '分类标签 · 过敏原管理', isDark),
),
const SizedBox(height: DesignTokens.space2),
_buildMessageItem('营销信息', '新品上线,立即查看', isDark),
],
);
}
Widget _buildMessageItem(String title, String subtitle, bool isDark) {
return Container(
padding: EdgeInsets.all(DesignTokens.space3),
decoration: BoxDecoration(
color: isDark ? DarkDesignTokens.card : DesignTokens.card,
borderRadius: DesignTokens.borderRadiusMd,
boxShadow: DesignTokens.shadowsSm,
),
child: Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: DesignTokens.dynamicPrimary,
),
),
const SizedBox(width: DesignTokens.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: DesignTokens.fontMd,
fontWeight: FontWeight.w600,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(
fontSize: DesignTokens.fontSm,
color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3,
),
),
],
),
),
Icon(
CupertinoIcons.chevron_right,
size: 16,
color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3,
),
],
),
);
}
Widget _buildAppFooter(bool isDark) {
return Column(
children: [
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: DesignTokens.dynamicPrimary.withValues(alpha: 0.2),
borderRadius: DesignTokens.borderRadiusSm,
),
),
const SizedBox(height: DesignTokens.space4),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('🍳 ', style: TextStyle(fontSize: DesignTokens.fontMd)),
Text(
'小妈厨房',
style: TextStyle(
fontSize: DesignTokens.fontLg,
fontWeight: FontWeight.w700,
color: isDark ? DarkDesignTokens.text1 : DesignTokens.text1,
),
),
],
),
const SizedBox(height: DesignTokens.space1),
Text(
'用心开发 · 只为更好的体验 ❤️',
style: TextStyle(
fontSize: DesignTokens.fontSm,
color: isDark ? DarkDesignTokens.text2 : DesignTokens.text2,
letterSpacing: 0.5,
),
),
SizedBox(height: DesignTokens.space2),
Text(
'v0.88.5 · 2026 Liquid Glass',
style: TextStyle(
fontSize: DesignTokens.fontXs,
color: isDark ? DarkDesignTokens.text3 : DesignTokens.text3,
),
),
SizedBox(height: DesignTokens.space3),
Text(
'使用Flutter SDk开发UI高度定制化的跨平台应用 ',
style: TextStyle(
fontSize: DesignTokens.fontXs,
color: DesignTokens.dynamicPrimary.withValues(alpha: 0.6),
),
),
],
);
}
}
class _FeatureItem {
final IconData icon;
final String label;
final Color color;
final String? route;
const _FeatureItem(this.icon, this.label, this.color, this.route);
}