feat: 饮食记录逻辑重构 + 通知管理分隔线修复 + 多页面 UI 调整
- 饮食: 新增 DietCommentaryPolicy + diet_record_logic, 饮食记录逻辑抽取复用 - 通知管理: 分隔线改为仿通知中心样式, 跳过图标区域只留文字下方 - 侧边栏: 对话记录操作栏浮层修复 + 常用功能间距收紧 - 智能体: 欢迎卡片去 400ms 延迟 + 胶囊去阴影 - 后端: AI 会话仓库新增方法 + 饮食评论策略 + 健康记录规则调整 - 其他: api_client IP 适配 + 多页面 UI 微调 + 新增测试
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../core/app_colors.dart';
|
||||
import 'app_gradient_widgets.dart';
|
||||
|
||||
class AppEmptyState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
@@ -32,7 +33,7 @@ class AppEmptyState extends StatelessWidget {
|
||||
border: Border.all(color: AppColors.borderLight),
|
||||
boxShadow: AppColors.cardShadowLight,
|
||||
),
|
||||
child: Icon(icon, size: 34, color: AppColors.primaryDark),
|
||||
child: AppGradientIcon(icon: icon, size: 34),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
Text(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../core/app_colors.dart';
|
||||
import 'app_gradient_widgets.dart';
|
||||
|
||||
/// 统一的"加载失败"状态——与 AppEmptyState 风格一致,区别在于明确表达"出错了,可重试",
|
||||
/// 避免把网络/服务失败误显示成"暂无数据"。
|
||||
@@ -32,10 +33,9 @@ class AppErrorState extends StatelessWidget {
|
||||
border: Border.all(color: AppColors.borderLight),
|
||||
boxShadow: AppColors.cardShadowLight,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.cloud_off_outlined,
|
||||
child: const AppGradientIcon(
|
||||
icon: Icons.cloud_off_outlined,
|
||||
size: 34,
|
||||
color: AppColors.primaryDark,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
@@ -61,17 +61,9 @@ class AppErrorState extends StatelessWidget {
|
||||
],
|
||||
if (onRetry != null) ...[
|
||||
const SizedBox(height: 20),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onRetry,
|
||||
icon: const Icon(Icons.refresh, size: 18),
|
||||
label: const Text('重试'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primaryDark,
|
||||
side: const BorderSide(color: AppColors.borderLight),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 116,
|
||||
child: AppGradientOutlineButton(label: '重试', onTap: onRetry!),
|
||||
),
|
||||
],
|
||||
],
|
||||
|
||||
93
health_app/lib/widgets/app_gradient_widgets.dart
Normal file
93
health_app/lib/widgets/app_gradient_widgets.dart
Normal file
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../core/app_colors.dart';
|
||||
|
||||
class AppGradientText extends StatelessWidget {
|
||||
final String text;
|
||||
final TextStyle style;
|
||||
final Gradient gradient;
|
||||
final TextAlign? textAlign;
|
||||
|
||||
const AppGradientText(
|
||||
this.text, {
|
||||
super.key,
|
||||
required this.style,
|
||||
this.gradient = AppColors.primaryGradient,
|
||||
this.textAlign,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShaderMask(
|
||||
shaderCallback: gradient.createShader,
|
||||
blendMode: BlendMode.srcIn,
|
||||
child: Text(
|
||||
text,
|
||||
textAlign: textAlign,
|
||||
style: style.copyWith(color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppGradientIcon extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final double size;
|
||||
final Gradient gradient;
|
||||
|
||||
const AppGradientIcon({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.size,
|
||||
this.gradient = AppColors.primaryGradient,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShaderMask(
|
||||
shaderCallback: gradient.createShader,
|
||||
blendMode: BlendMode.srcIn,
|
||||
child: Icon(icon, size: size, color: Colors.white),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppGradientOutlineButton extends StatelessWidget {
|
||||
final String label;
|
||||
final VoidCallback onTap;
|
||||
final Gradient gradient;
|
||||
|
||||
const AppGradientOutlineButton({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
this.gradient = AppColors.primaryGradient,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
gradient: gradient,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
padding: const EdgeInsets.all(1.2),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(17),
|
||||
),
|
||||
child: AppGradientText(
|
||||
label,
|
||||
gradient: gradient,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w800),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -380,25 +380,25 @@ class _NavigationSection extends StatelessWidget {
|
||||
icon: LucideIcons.folderHeart,
|
||||
title: '档案',
|
||||
route: 'healthArchive',
|
||||
colors: const [Color(0xFF93C5FD), Color(0xFF60A5FA)],
|
||||
colors: AppColors.healthGradient.colors,
|
||||
),
|
||||
_NavItem(
|
||||
icon: LucideIcons.fileText,
|
||||
title: '报告',
|
||||
route: 'reports',
|
||||
colors: const [Color(0xFFBFDBFE), Color(0xFF93C5FD)],
|
||||
colors: AppColors.reportGradient.colors,
|
||||
),
|
||||
_NavItem(
|
||||
icon: LucideIcons.pill,
|
||||
title: '用药',
|
||||
route: 'medications',
|
||||
colors: const [Color(0xFFDDD6FE), Color(0xFFC4B5FD)],
|
||||
colors: AppColors.medicationGradient.colors,
|
||||
),
|
||||
_NavItem(
|
||||
icon: LucideIcons.utensils,
|
||||
title: '饮食',
|
||||
route: 'dietRecords',
|
||||
colors: const [Color(0xFFFBCFE8), Color(0xFFF472B6)],
|
||||
colors: AppColors.dietGradient.colors,
|
||||
),
|
||||
_NavItem(
|
||||
icon: LucideIcons.calendarDays,
|
||||
@@ -416,13 +416,13 @@ class _NavigationSection extends StatelessWidget {
|
||||
icon: LucideIcons.footprints,
|
||||
title: '运动',
|
||||
route: 'exercisePlan',
|
||||
colors: const [Color(0xFFA5F3FC), Color(0xFF67E8F9)],
|
||||
colors: AppColors.exerciseGradient.colors,
|
||||
),
|
||||
_NavItem(
|
||||
icon: LucideIcons.bluetooth,
|
||||
title: '设备',
|
||||
route: 'devices',
|
||||
colors: const [Color(0xFF60A5FA), Color(0xFFC4B5FD)],
|
||||
colors: AppColors.deviceGradient.colors,
|
||||
),
|
||||
];
|
||||
|
||||
@@ -466,14 +466,14 @@ class _NavTile extends StatelessWidget {
|
||||
};
|
||||
|
||||
List<Color> get _colors => switch (item.route) {
|
||||
'healthArchive' => const [AppColors.healthLight, AppColors.health],
|
||||
'reports' => const [AppColors.reportLight, AppColors.report],
|
||||
'medications' => const [AppColors.medicationLight, AppColors.medication],
|
||||
'dietRecords' => const [AppColors.dietLight, AppColors.diet],
|
||||
'healthArchive' => AppColors.healthGradient.colors,
|
||||
'reports' => AppColors.reportGradient.colors,
|
||||
'medications' => AppColors.medicationGradient.colors,
|
||||
'dietRecords' => AppColors.dietGradient.colors,
|
||||
'calendar' => const [AppColors.calendarLight, AppColors.calendar],
|
||||
'followups' => const [AppColors.followupLight, AppColors.followup],
|
||||
'exercisePlan' => const [AppColors.exerciseLight, AppColors.exercise],
|
||||
'devices' => const [AppColors.deviceLight, AppColors.device],
|
||||
'exercisePlan' => AppColors.exerciseGradient.colors,
|
||||
'devices' => AppColors.deviceGradient.colors,
|
||||
_ => item.colors,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user