feat: UI 清新风全面改造 — 朝露主题 + shadcn_ui + 全局字号放大

- 全新"朝露"主题:深青绿主色(#2D6A4F),暖白底,完整设计Token
- 引入 shadcn_ui 组件库,MaterialApp 注入 ShadTheme
- 新增 4 个公共组件:AppCard、AppMenuItem、AppEmptyState、AppTabChip
- 全面消除硬编码颜色,统一使用 AppTheme Token
- 全局字号 +3~4pt,图标等比放大 +3px
- home/medication/profile/settings/login 等核心页面重写
- 路由和功能逻辑零改动
This commit is contained in:
MingNian
2026-06-08 18:06:18 +08:00
parent 16d1d3d305
commit 58af5f6d5b
28 changed files with 1616 additions and 812 deletions

View File

@@ -1,14 +1,16 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:file_picker/file_picker.dart';
import 'dart:io';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../core/app_theme.dart';
import '../../core/navigation_provider.dart';
import '../../providers/auth_provider.dart';
import '../../providers/chat_provider.dart';
import '../../providers/data_providers.dart';
import '../diet/diet_capture_page.dart';
import '../../core/navigation_provider.dart';
import '../../widgets/health_drawer.dart';
import '../diet/diet_capture_page.dart';
import 'widgets/chat_messages_view.dart';
class HomePage extends ConsumerStatefulWidget {
@@ -21,9 +23,10 @@ class _HomePageState extends ConsumerState<HomePage> {
final _scrollCtrl = ScrollController();
String? _pickedImagePath;
final Set<ActiveAgent> _welcomedAgents = {};
final _focusNode = FocusNode();
@override void initState() { super.initState(); }
@override void dispose() { _textCtrl.dispose(); _scrollCtrl.dispose(); super.dispose(); }
@override void dispose() { _textCtrl.dispose(); _scrollCtrl.dispose(); _focusNode.dispose(); super.dispose(); }
void _sendMessage() {
final text = _textCtrl.text.trim();
@@ -43,62 +46,60 @@ class _HomePageState extends ConsumerState<HomePage> {
final auth = ref.watch(authProvider);
final user = auth.user;
final selectedAgent = ref.watch(selectedAgentProvider);
final theme = ShadTheme.of(context);
ref.listen(cameraActionProvider, (prev, next) {
if (next == 'camera') {
_pickImage(ImageSource.camera);
ref.read(cameraActionProvider.notifier).clear();
} else if (next == 'gallery') {
_pickImage(ImageSource.gallery);
ref.read(cameraActionProvider.notifier).clear();
}
if (next == 'camera') { _pickImage(ImageSource.camera); ref.read(cameraActionProvider.notifier).clear(); }
else if (next == 'gallery') { _pickImage(ImageSource.gallery); ref.read(cameraActionProvider.notifier).clear(); }
});
ref.listen(dietActionProvider, (prev, next) {
if (next == 'pickFoodCamera') {
_pickFoodImage(ImageSource.camera);
ref.read(dietActionProvider.notifier).clear();
} else if (next == 'pickFoodGallery') {
_pickFoodImage(ImageSource.gallery);
ref.read(dietActionProvider.notifier).clear();
}
if (next == 'pickFoodCamera') { _pickFoodImage(ImageSource.camera); ref.read(dietActionProvider.notifier).clear(); }
else if (next == 'pickFoodGallery') { _pickFoodImage(ImageSource.gallery); ref.read(dietActionProvider.notifier).clear(); }
});
return Scaffold(
drawer: const HealthDrawer(),
backgroundColor: const Color(0xFFF8F9FC),
backgroundColor: theme.colorScheme.background,
body: SafeArea(
child: Column(children: [
// ── 顶部栏 ──
_buildHeader(user),
// ── 聊天区域(今日任务在对话流最上方)──
_buildHeader(user, theme),
Expanded(child: ChatMessagesView(scrollCtrl: _scrollCtrl, messages: chatState.messages)),
// ── 底部合并区 ──
_buildBottomBar(context, selectedAgent),
_buildBottomBar(context, selectedAgent, theme),
]),
),
);
}
// ═════════════════════ 顶部栏 ═════════════════════
Widget _buildHeader(dynamic user) {
// ═══════ 顶部栏 ═══════
Widget _buildHeader(dynamic user, ShadThemeData theme) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: AppTheme.sLg, vertical: 10),
decoration: BoxDecoration(
color: theme.colorScheme.card,
border: Border(bottom: BorderSide(color: theme.colorScheme.border, width: 0.5)),
),
child: Row(children: [
Builder(builder: (ctx) => GestureDetector(
onTap: () => Scaffold.of(ctx).openDrawer(),
child: CircleAvatar(radius: 20, backgroundColor: const Color(0xFFF0F2FF), backgroundImage: user?.avatarUrl != null ? NetworkImage(user!.avatarUrl!) : null, child: user?.avatarUrl == null ? const Icon(Icons.person, size: 24, color: Color(0xFF8B9CF7)) : null),
child: CircleAvatar(
radius: 20,
backgroundColor: AppTheme.primaryLight,
backgroundImage: user?.avatarUrl != null ? NetworkImage(user!.avatarUrl!) : null,
child: user?.avatarUrl == null ? const Icon(Icons.person, size: 28, color: AppTheme.primary) : null,
),
)),
const SizedBox(width: 10),
Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(mainAxisSize: MainAxisSize.min, children: [Icon(Icons.smart_toy_outlined, size: 16, color: const Color(0xFF8B9CF7)), const SizedBox(width: 4), Text('AI 健康管家', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: Colors.grey[600]))]),
Row(mainAxisSize: MainAxisSize.min, children: [
Icon(LucideIcons.bot, size: 19, color: AppTheme.primary),
const SizedBox(width: 4),
Text('AI 健康管家', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: theme.colorScheme.mutedForeground)),
]),
const SizedBox(height: 2),
Text('${_getGreeting()}${(user?.name != null && user!.name!.isNotEmpty) ? user.name : '用户'}', style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold, color: Color(0xFF1A1A1A))),
Text('${_getGreeting()}${(user?.name != null && user!.name!.isNotEmpty) ? user.name : '用户'}',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: theme.colorScheme.foreground)),
])),
Icon(Icons.notifications_none, size: 22, color: Colors.grey[600]),
Icon(LucideIcons.bell, size: 25, color: theme.colorScheme.mutedForeground),
]),
);
}
@@ -111,21 +112,20 @@ class _HomePageState extends ConsumerState<HomePage> {
return '晚上好';
}
// ═════════════════════ 智能体选择条(常驻) ═════════════════════
// ═══════ 智能体选择条 ═══════
static final _agentDefs = [
(ActiveAgent.consultation, '问诊', Icons.chat_bubble_outline),
(ActiveAgent.health, '记数据', Icons.favorite_border),
(ActiveAgent.diet, '拍饮食', Icons.restaurant_outlined),
(ActiveAgent.medication, '药管家', Icons.medication_outlined),
(ActiveAgent.report, '看报告', Icons.description_outlined),
(ActiveAgent.exercise, '运动', Icons.directions_run_outlined),
(ActiveAgent.consultation, '问诊', LucideIcons.messageCircle),
(ActiveAgent.health, '记数据', LucideIcons.heart),
(ActiveAgent.diet, '拍饮食', LucideIcons.utensils),
(ActiveAgent.medication, '药管家', LucideIcons.pill),
(ActiveAgent.report, '看报告', LucideIcons.fileText),
(ActiveAgent.exercise, '运动', LucideIcons.trendingUp),
];
Widget _buildAgentBar(ActiveAgent? selected) {
Widget _buildAgentBar(ActiveAgent? selected, ShadThemeData theme) {
return Container(
height: 36,
padding: const EdgeInsets.symmetric(horizontal: 12),
height: 40,
padding: const EdgeInsets.symmetric(horizontal: AppTheme.sMd),
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: _agentDefs.length,
@@ -146,17 +146,18 @@ class _HomePageState extends ConsumerState<HomePage> {
}
}
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isActive ? const Color(0xFF8B9CF7) : Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: isActive ? const Color(0xFF8B9CF7) : const Color(0xFFE0E0E0)),
color: isActive ? AppTheme.primary : theme.colorScheme.card,
borderRadius: BorderRadius.circular(AppTheme.rPill),
boxShadow: isActive ? [AppTheme.shadowLight] : null,
),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Icon(icon, size: 13, color: isActive ? Colors.white : const Color(0xFF666666)),
const SizedBox(width: 3),
Text(label, style: TextStyle(fontSize: 11, fontWeight: isActive ? FontWeight.w600 : FontWeight.w500, color: isActive ? Colors.white : const Color(0xFF666666))),
Icon(icon, size: 17, color: isActive ? Colors.white : theme.colorScheme.mutedForeground),
const SizedBox(width: 4),
Text(label, style: TextStyle(fontSize: 15, fontWeight: isActive ? FontWeight.w600 : FontWeight.w500, color: isActive ? Colors.white : theme.colorScheme.mutedForeground)),
]),
),
);
@@ -165,59 +166,137 @@ class _HomePageState extends ConsumerState<HomePage> {
);
}
// ═════════════════════ 底部合并区:智能体栏 + 操作面板 + 输入框 ═════════════════════
Widget _buildBottomBar(BuildContext context, ActiveAgent? selectedAgent) {
// ═══════ 底部区域 ═══════
Widget _buildBottomBar(BuildContext context, ActiveAgent? selectedAgent, ShadThemeData theme) {
return Column(mainAxisSize: MainAxisSize.min, children: [
// 智能体胶囊栏常驻高度36
_buildAgentBar(selectedAgent),
// 图片预览(有选中图片时显示)
if (_pickedImagePath != null) _buildImagePreview(),
// 输入框
_buildCompactInputBar(context),
Container(
padding: const EdgeInsets.only(top: AppTheme.sSm, bottom: 6),
child: _buildAgentBar(selectedAgent, theme),
),
if (_pickedImagePath != null) _buildImagePreview(theme),
_buildCompactInputBar(theme),
]);
}
Widget _buildImagePreview() {
Widget _buildImagePreview(ShadThemeData theme) {
return Container(
padding: const EdgeInsets.fromLTRB(12, 8, 12, 4),
decoration: const BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: Color(0xFFEEEEEE)))),
decoration: BoxDecoration(
color: theme.colorScheme.card,
border: Border(top: BorderSide(color: theme.colorScheme.border)),
),
child: Row(children: [
Stack(children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(AppTheme.rXs),
child: Image.file(File(_pickedImagePath!), width: 60, height: 60, fit: BoxFit.cover),
),
Positioned(top: -4, right: -4, child: GestureDetector(
onTap: () => setState(() => _pickedImagePath = null),
child: Container(width: 20, height: 20, decoration: const BoxDecoration(color: Color(0xFF333333), shape: BoxShape.circle), child: const Icon(Icons.close, size: 14, color: Colors.white)),
child: Container(
width: 20, height: 20,
decoration: const BoxDecoration(color: AppTheme.text, shape: BoxShape.circle),
child: const Icon(Icons.close, size: 17, color: Colors.white),
),
)),
]),
const Spacer(),
Text('点击发送上传图片', style: TextStyle(fontSize: 12, color: Colors.grey[500])),
Text('点击发送上传图片', style: TextStyle(fontSize: 15, color: theme.colorScheme.mutedForeground)),
]),
);
}
Widget _buildCompactInputBar(BuildContext context) {
Widget _buildCompactInputBar(ShadThemeData theme) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
decoration: BoxDecoration(color: Colors.white, border: Border(top: BorderSide(color: const Color(0xFFEEEEEE)))),
child: Row(children: [
IconButton(icon: const Icon(Icons.attach_file, size: 24, color: Color(0xFF666666)), onPressed: () => _showAttachmentPicker(context)),
Expanded(child: TextField(
controller: _textCtrl,
style: const TextStyle(fontSize: 15),
decoration: const InputDecoration(hintText: '输入你想说的...', hintStyle: TextStyle(fontSize: 15, color: Color(0xFFBBBBBB)), contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), border: InputBorder.none),
onSubmitted: (_) => _sendMessage(),
)),
IconButton(icon: const Icon(Icons.send, size: 24, color: Color(0xFF8B9CF7)), onPressed: _sendMessage),
padding: const EdgeInsets.symmetric(horizontal: AppTheme.sSm, vertical: AppTheme.sSm),
decoration: BoxDecoration(
color: theme.colorScheme.card,
border: Border(top: BorderSide(color: theme.colorScheme.border)),
),
child: Row(crossAxisAlignment: CrossAxisAlignment.end, children: [
ShadButton.ghost(
size: ShadButtonSize.sm,
leading: Icon(LucideIcons.paperclip, size: 25, color: theme.colorScheme.mutedForeground),
onPressed: () => _showAttachmentPicker(context, theme),
),
Expanded(
child: TextField(
controller: _textCtrl,
focusNode: _focusNode,
style: TextStyle(fontSize: 18, color: theme.colorScheme.foreground),
maxLines: null,
textInputAction: TextInputAction.newline,
decoration: InputDecoration(
hintText: '输入你想说的...',
hintStyle: TextStyle(fontSize: 18, color: theme.colorScheme.mutedForeground),
filled: true,
fillColor: theme.colorScheme.muted,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppTheme.rXl),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppTheme.rXl),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppTheme.rXl),
borderSide: BorderSide.none,
),
),
onSubmitted: (_) => _sendMessage(),
),
),
const SizedBox(width: 4),
ShadButton(
size: ShadButtonSize.sm,
leading: Icon(LucideIcons.send, size: 21, color: theme.colorScheme.primaryForeground),
onPressed: _sendMessage,
),
]),
);
}
void _showAttachmentPicker(BuildContext context, ShadThemeData theme) {
showModalBottomSheet(
context: context,
backgroundColor: theme.colorScheme.card,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(AppTheme.rXl)),
),
builder: (ctx) => SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: AppTheme.sMd),
child: Wrap(children: [
ListTile(
leading: Icon(LucideIcons.camera, color: theme.colorScheme.foreground),
title: Text('拍照', style: TextStyle(color: theme.colorScheme.foreground)),
onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.camera); },
),
ListTile(
leading: Icon(LucideIcons.image, color: theme.colorScheme.foreground),
title: Text('从相册选', style: TextStyle(color: theme.colorScheme.foreground)),
onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.gallery); },
),
ListTile(
leading: Icon(LucideIcons.file, color: theme.colorScheme.foreground),
title: Text('传文件', style: TextStyle(color: theme.colorScheme.foreground)),
onTap: () async {
Navigator.pop(ctx);
final result = await FilePicker.platform.pickFiles();
if (result != null && result.files.isNotEmpty) {
_textCtrl.text = '[文件已选择] ${result.files.first.name}';
if (mounted) setState(() {});
}
},
),
]),
),
),
);
}
Future<void> _pickImage(ImageSource source) async {
final picker = ImagePicker();
final picked = await picker.pickImage(source: source, imageQuality: 85);
@@ -238,13 +317,4 @@ class _HomePageState extends ConsumerState<HomePage> {
pushRoute(ref, 'dietCapture');
}
}
void _showAttachmentPicker(BuildContext context) {
showModalBottomSheet(context: context, builder: (ctx) => SafeArea(child: Wrap(children: [
ListTile(leading: const Icon(Icons.camera_alt), title: const Text('拍照'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.camera); }),
ListTile(leading: const Icon(Icons.photo_library), title: const Text('从相册选'), onTap: () { Navigator.pop(ctx); _pickImage(ImageSource.gallery); }),
ListTile(leading: const Icon(Icons.attach_file), title: const Text('传文件'), onTap: () async { Navigator.pop(ctx); final result = await FilePicker.platform.pickFiles(); if (result != null && result.files.isNotEmpty) { _textCtrl.text = '[文件已选择] ${result.files.first.name}'; if (mounted) setState(() {}); }}),
])));
}
}

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/app_theme.dart';
import '../../../core/navigation_provider.dart';
import '../../../providers/auth_provider.dart';
import '../../../providers/chat_provider.dart';
@@ -27,15 +28,15 @@ class ChatMessagesView extends ConsumerWidget {
width: 80,
height: 80,
decoration: BoxDecoration(
color: const Color(0xFFF0F2FF),
color: AppTheme.primaryLight,
borderRadius: BorderRadius.circular(40),
),
child: const Icon(Icons.health_and_safety, size: 40, color: Color(0xFF8B9CF7)),
child: const Icon(Icons.health_and_safety, size: 40, color: AppTheme.primary),
),
const SizedBox(height: 16),
Text('开始和 AI 健康管家对话吧', style: Theme.of(context).textTheme.bodyMedium),
const SizedBox(height: 8),
const Text('记录健康数据,获取专业建议', style: TextStyle(fontSize: 14, color: Color(0xFF9E9E9E))),
const Text('记录健康数据,获取专业建议', style: TextStyle(fontSize: 17, color: Color(0xFF9E9E9E))),
],
),
);
@@ -135,7 +136,7 @@ class ChatMessagesView extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(info.$2, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700, color: Colors.white)),
Text(info.$2, style: const TextStyle(fontSize: 21, fontWeight: FontWeight.w700, color: Colors.white)),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3),
@@ -143,7 +144,7 @@ class ChatMessagesView extends ConsumerWidget {
color: Colors.white.withAlpha(25),
borderRadius: BorderRadius.circular(10),
),
child: Text(info.$3, style: const TextStyle(fontSize: 12, color: Color(0xFFE8E6FF))),
child: Text(info.$3, style: const TextStyle(fontSize: 15, color: Color(0xFFE8E6FF))),
),
],
),
@@ -210,10 +211,10 @@ class ChatMessagesView extends ConsumerWidget {
color: colors.iconBg,
borderRadius: BorderRadius.circular(11),
),
child: Icon(a.icon, size: 20, color: colors.accent),
child: Icon(a.icon, size: 23, color: colors.accent),
),
const SizedBox(height: 7),
Text(a.label, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF333333))),
Text(a.label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Color(0xFF333333))),
],
),
),
@@ -245,30 +246,30 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFF0F2FF)),
border: Border.all(color: AppTheme.primaryLight),
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
CircleAvatar(
radius: 22,
backgroundColor: const Color(0xFFF0F2FF),
child: Text(doc['name']![0], style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF8B9CF7))),
backgroundColor: AppTheme.primaryLight,
child: Text(doc['name']![0], style: const TextStyle(fontSize: 21, fontWeight: FontWeight.w600, color: AppTheme.primary)),
),
const SizedBox(height: 6),
Text(doc['name']!, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF333333))),
Text(doc['title']!, style: const TextStyle(fontSize: 11, color: Color(0xFF999999))),
Text(doc['name']!, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF333333))),
Text(doc['title']!, style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
const SizedBox(height: 2),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFFF0F2FF),
color: AppTheme.primaryLight,
borderRadius: BorderRadius.circular(4),
),
child: Text(doc['dept']!, style: const TextStyle(fontSize: 10, color: Color(0xFF8B9CF7))),
child: Text(doc['dept']!, style: const TextStyle(fontSize: 13, color: AppTheme.primary)),
),
const SizedBox(height: 4),
Text(
doc['desc']!,
style: const TextStyle(fontSize: 10, color: Color(0xFF888888), height: 1.3),
style: const TextStyle(fontSize: 13, color: Color(0xFF888888), height: 1.3),
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
@@ -298,34 +299,34 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF6C5CE7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(mainAxisSize: MainAxisSize.min, children: [
Container(
width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 10),
decoration: const BoxDecoration(gradient: LinearGradient(colors: [Color(0xFF4CAF50), Color(0xFF43A047)])),
decoration: const BoxDecoration(gradient: LinearGradient(colors: [Color(0xFF4CAF50), AppTheme.success])),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
const Icon(Icons.info_outline, size: 18, color: Colors.white), const SizedBox(width: 6),
const Icon(Icons.info_outline, size: 21, color: Colors.white), const SizedBox(width: 6),
Text(metricType == 'exercise' ? '请确认运动计划' : '请确认录入',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)),
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600, color: Colors.white)),
]),
),
Padding(padding: const EdgeInsets.all(18), child: Column(children: [
Align(alignment: Alignment.centerLeft, child: Text(recordTime.isNotEmpty ? recordTime : _formatTime(msg.createdAt), style: const TextStyle(fontSize: 12, color: Color(0xFF9E9E9E)))),
Align(alignment: Alignment.centerLeft, child: Text(recordTime.isNotEmpty ? recordTime : _formatTime(msg.createdAt), style: const TextStyle(fontSize: 15, color: Color(0xFF9E9E9E)))),
const SizedBox(height: 14),
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(color: const Color(0xFFF9F8FF), borderRadius: BorderRadius.circular(16)),
child: Row(children: [
Container(width: 52, height: 52, decoration: BoxDecoration(color: const Color(0xFFEDEAFF), borderRadius: BorderRadius.circular(14)), child: Center(child: Text(_getMetricIcon(metricType), style: const TextStyle(fontSize: 26)))),
Container(width: 52, height: 52, decoration: BoxDecoration(color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(14)), child: Center(child: Text(_getMetricIcon(metricType), style: const TextStyle(fontSize: 30)))),
const SizedBox(width: 14),
Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(_getMetricName(metricType), style: const TextStyle(fontSize: 13, color: Color(0xFF888888))),
Text(_getMetricName(metricType), style: const TextStyle(fontSize: 16, color: Color(0xFF888888))),
const SizedBox(height: 4),
RichText(text: TextSpan(children: [
TextSpan(text: value, style: TextStyle(fontSize: 28, fontWeight: FontWeight.w800, color: abnormal ? const Color(0xFFE53935) : const Color(0xFF1A1A2E))),
TextSpan(text: ' $unit', style: TextStyle(fontSize: 14, color: abnormal ? const Color(0xFFE53970) : const Color(0xFF999999))),
TextSpan(text: value, style: TextStyle(fontSize: 32, fontWeight: FontWeight.w800, color: abnormal ? AppTheme.error : const Color(0xFF1A1A2E))),
TextSpan(text: ' $unit', style: TextStyle(fontSize: 17, color: abnormal ? const Color(0xFFE53970) : const Color(0xFF999999))),
])),
])),
]),
@@ -333,7 +334,7 @@ class ChatMessagesView extends ConsumerWidget {
if (abnormal) ...[
const SizedBox(height: 12),
Container(width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration(color: const Color(0xFFFFF3F0), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFFFDAD4))),
child: const Row(children: [Icon(Icons.warning_amber_rounded, size: 18, color: Color(0xFFE53935)), SizedBox(width: 8), Expanded(child: Text('数值偏高,建议关注', style: TextStyle(fontSize: 13, color: Color(0xFFE53935), fontWeight: FontWeight.w500)))])),
child: const Row(children: [Icon(Icons.warning_amber_rounded, size: 21, color: AppTheme.error), SizedBox(width: 8), Expanded(child: Text('数值偏高,建议关注', style: TextStyle(fontSize: 16, color: AppTheme.error, fontWeight: FontWeight.w500)))])),
],
const SizedBox(height: 18),
if (msg.confirmed)
@@ -341,12 +342,12 @@ class ChatMessagesView extends ConsumerWidget {
width: double.infinity,
child: ElevatedButton.icon(
onPressed: null,
icon: const Icon(Icons.check_circle, size: 18),
icon: const Icon(Icons.check_circle, size: 21),
label: const Text('录入成功'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF43A047),
backgroundColor: AppTheme.success,
foregroundColor: Colors.white,
disabledBackgroundColor: const Color(0xFF43A047),
disabledBackgroundColor: AppTheme.success,
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
padding: const EdgeInsets.symmetric(vertical: 12),
@@ -388,7 +389,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -399,7 +400,7 @@ class ChatMessagesView extends ConsumerWidget {
width: double.infinity,
padding: const EdgeInsets.fromLTRB(18, 20, 18, 16),
decoration: const BoxDecoration(
gradient: LinearGradient(colors: [Color(0xFFE8F0FE), Color(0xFFF0F2FF)]),
gradient: LinearGradient(colors: [Color(0xFFE8F0FE), AppTheme.primaryLight]),
),
child: Row(
children: [
@@ -410,17 +411,17 @@ class ChatMessagesView extends ConsumerWidget {
color: const Color(0xFFFFF3E0),
borderRadius: BorderRadius.circular(13),
),
child: const Center(child: Text('💊', style: TextStyle(fontSize: 24))),
child: const Center(child: Text('💊', style: TextStyle(fontSize: 28))),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w700, color: Color(0xFF1A1A2E))),
Text(name, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: Color(0xFF1A1A2E))),
if (dosage.isNotEmpty) ...[
const SizedBox(height: 3),
Text(dosage, style: const TextStyle(fontSize: 13, color: Color(0xFF777777))),
Text(dosage, style: const TextStyle(fontSize: 16, color: Color(0xFF777777))),
],
],
),
@@ -444,12 +445,12 @@ class ChatMessagesView extends ConsumerWidget {
width: double.infinity,
child: ElevatedButton.icon(
onPressed: null,
icon: const Icon(Icons.check_circle, size: 18),
icon: const Icon(Icons.check_circle, size: 21),
label: const Text('录入成功'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF43A047),
backgroundColor: AppTheme.success,
foregroundColor: Colors.white,
disabledBackgroundColor: const Color(0xFF43A047),
disabledBackgroundColor: AppTheme.success,
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
padding: const EdgeInsets.symmetric(vertical: 12),
@@ -479,9 +480,9 @@ class ChatMessagesView extends ConsumerWidget {
padding: const EdgeInsets.only(bottom: 10),
child: Row(
children: [
Icon(icon, size: 17, color: const Color(0xFF888888)),
Icon(icon, size: 20, color: const Color(0xFF888888)),
const SizedBox(width: 8),
Text(text, style: const TextStyle(fontSize: 13, color: Color(0xFF444444))),
Text(text, style: const TextStyle(fontSize: 16, color: Color(0xFF444444))),
],
),
);
@@ -514,7 +515,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -526,8 +527,8 @@ class ChatMessagesView extends ConsumerWidget {
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: const BoxDecoration(gradient: LinearGradient(colors: [Color(0xFFFFF8E1), Color(0xFFFFF3E0)])),
child: const Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text('🍽️ ', style: TextStyle(fontSize: 18)),
Text('饮食分析结果', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Color(0xFF1A1A2E))),
Text('🍽️ ', style: TextStyle(fontSize: 21)),
Text('饮食分析结果', style: TextStyle(fontSize: 19, fontWeight: FontWeight.w700, color: Color(0xFF1A1A2E))),
]),
),
Padding(
@@ -536,15 +537,15 @@ class ChatMessagesView extends ConsumerWidget {
// ── 总热量(仅 >0 时显示) ──
if (totalCalories > 0) ...[
Center(child: Column(children: [
Text('$totalCalories', style: const TextStyle(fontSize: 36, fontWeight: FontWeight.w800, color: Color(0xFFFF8F00))),
const Text('千卡 (kcal)', style: TextStyle(fontSize: 12, color: Color(0xFFAAAAAA))),
Text('$totalCalories', style: const TextStyle(fontSize: 40, fontWeight: FontWeight.w800, color: Color(0xFFFF8F00))),
const Text('千卡 (kcal)', style: TextStyle(fontSize: 15, color: Color(0xFFAAAAAA))),
])),
const SizedBox(height: 16),
],
// ── 识别食物列表 ──
if (foods.isNotEmpty) ...[
const Text('识别结果', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
const Text('识别结果', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
const SizedBox(height: 10),
...foods.map((food) {
final f = food is Map ? food : <String, dynamic>{};
@@ -559,11 +560,11 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(color: const Color(0xFFFAFAFA), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFFF0F0F0))),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
Expanded(child: Text(name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF1A1A1A)))),
if (calories > 0) Text('${calories is int ? calories : calories.toInt()} kcal', style: const TextStyle(fontSize: 13, color: Color(0xFF888888))),
Expanded(child: Text(name, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: Color(0xFF1A1A1A)))),
if (calories > 0) Text('${calories is int ? calories : calories.toInt()} kcal', style: const TextStyle(fontSize: 16, color: Color(0xFF888888))),
]),
if (portion != null && portion.isNotEmpty) Padding(padding: const EdgeInsets.only(top: 4), child: Text(portion, style: TextStyle(fontSize: 12, color: Colors.grey[500]))),
if (nutrients != null && nutrients.isNotEmpty) Padding(padding: const EdgeInsets.only(top: 2), child: Text(nutrients, style: TextStyle(fontSize: 11, color: Colors.grey[500]))),
if (portion != null && portion.isNotEmpty) Padding(padding: const EdgeInsets.only(top: 4), child: Text(portion, style: TextStyle(fontSize: 15, color: Colors.grey[500]))),
if (nutrients != null && nutrients.isNotEmpty) Padding(padding: const EdgeInsets.only(top: 2), child: Text(nutrients, style: TextStyle(fontSize: 14, color: Colors.grey[500]))),
]),
);
}),
@@ -572,12 +573,12 @@ class ChatMessagesView extends ConsumerWidget {
// ── AI 建议 ──
const SizedBox(height: 14),
const Text('AI 建议', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
const Text('AI 建议', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(color: const Color(0xFFF0F2FF), borderRadius: BorderRadius.circular(10)),
child: Text(advice, style: const TextStyle(fontSize: 13, height: 1.6, color: Color(0xFF555555))),
decoration: BoxDecoration(color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(10)),
child: Text(advice, style: const TextStyle(fontSize: 16, height: 1.6, color: Color(0xFF555555))),
),
]),
),
@@ -606,7 +607,7 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
clipBehavior: Clip.antiAlias,
child: Column(
@@ -628,16 +629,16 @@ class ChatMessagesView extends ConsumerWidget {
color: const Color(0xFFC5CAE9),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.description_outlined, size: 20, color: Color(0xFF3F51B5)),
child: const Icon(Icons.description_outlined, size: 23, color: Color(0xFF3F51B5)),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(reportType, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Color(0xFF1A1A2E))),
Text(reportType, style: const TextStyle(fontSize: 19, fontWeight: FontWeight.w700, color: Color(0xFF1A1A2E))),
if (reportDate.isNotEmpty)
Text(reportDate, style: const TextStyle(fontSize: 12, color: Color(0xFF888888))),
Text(reportDate, style: const TextStyle(fontSize: 15, color: Color(0xFF888888))),
],
),
),
@@ -668,9 +669,9 @@ class ChatMessagesView extends ConsumerWidget {
),
child: const Row(
children: [
Expanded(flex: 2, child: Text('指标名称', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xFF666666)))),
Expanded(flex: 1, child: Text('数值', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xFF666666)), textAlign: TextAlign.center)),
Expanded(flex: 1, child: Text('状态', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xFF666666)), textAlign: TextAlign.center)),
Expanded(flex: 2, child: Text('指标名称', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF666666)))),
Expanded(flex: 1, child: Text('数值', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF666666)), textAlign: TextAlign.center)),
Expanded(flex: 1, child: Text('状态', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF666666)), textAlign: TextAlign.center)),
],
),
),
@@ -684,9 +685,9 @@ class ChatMessagesView extends ConsumerWidget {
final isAbnormal = status != 'normal';
Color sc;
switch (status) {
case 'high': sc = const Color(0xFFE53935); break;
case 'high': sc = AppTheme.error; break;
case 'low': sc = const Color(0xFFF9A825); break;
default: sc = const Color(0xFF43A047);
default: sc = AppTheme.success;
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
@@ -703,12 +704,12 @@ class ChatMessagesView extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: TextStyle(fontSize: 13, color: isAbnormal ? const Color(0xFFE53935) : const Color(0xFF333333), fontWeight: isAbnormal ? FontWeight.w600 : FontWeight.normal)),
if (refRange.isNotEmpty) Text('参考:$refRange', style: const TextStyle(fontSize: 10, color: Color(0xFFAAAAAA))),
Text(name, style: TextStyle(fontSize: 16, color: isAbnormal ? AppTheme.error : const Color(0xFF333333), fontWeight: isAbnormal ? FontWeight.w600 : FontWeight.normal)),
if (refRange.isNotEmpty) Text('参考:$refRange', style: const TextStyle(fontSize: 13, color: Color(0xFFAAAAAA))),
],
),
),
Expanded(flex: 1, child: Text(value, textAlign: TextAlign.center, style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: sc))),
Expanded(flex: 1, child: Text(value, textAlign: TextAlign.center, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: sc))),
Expanded(
flex: 1,
child: Center(
@@ -743,13 +744,13 @@ class ChatMessagesView extends ConsumerWidget {
children: [
const Row(
children: [
Icon(Icons.auto_awesome, size: 16, color: Color(0xFF8B9CF7)),
Icon(Icons.auto_awesome, size: 19, color: AppTheme.primary),
SizedBox(width: 6),
Text('AI 解读摘要', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF8B9CF7))),
Text('AI 解读摘要', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.primary)),
],
),
const SizedBox(height: 8),
Text(summary, style: const TextStyle(fontSize: 13, color: Color(0xFF555555), height: 1.5)),
Text(summary, style: const TextStyle(fontSize: 16, color: Color(0xFF555555), height: 1.5)),
],
),
),
@@ -786,27 +787,27 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(15), blurRadius: 14, offset: const Offset(0, 4))],
),
child: Padding(
padding: const EdgeInsets.fromLTRB(18, 16, 18, 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(msg.content, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: Color(0xFF1A1A2E))),
Text(msg.content, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500, color: Color(0xFF1A1A2E))),
const SizedBox(height: 14),
Wrap(spacing: 8, runSpacing: 8, children: options.map((opt) {
final o = opt as Map? ?? {};
return ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFF0F2FF),
foregroundColor: const Color(0xFF8B9CF7),
backgroundColor: AppTheme.primaryLight,
foregroundColor: AppTheme.primary,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 11),
),
child: Text(o['label'] as String? ?? '', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500)),
child: Text(o['label'] as String? ?? '', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
);
}).toList()),
],
@@ -830,7 +831,7 @@ class ChatMessagesView extends ConsumerWidget {
color: const Color(0xFFFEFEFF),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(4), topRight: Radius.circular(20), bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
border: Border.all(color: const Color(0xFFD8DCFD), width: 1.5),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -840,13 +841,13 @@ class ChatMessagesView extends ConsumerWidget {
height: 26,
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: const Color(0xFFF0F2FF),
color: AppTheme.primaryLight,
borderRadius: BorderRadius.circular(13),
),
child: const CircularProgressIndicator(strokeWidth: 2.2, color: Color(0xFF8B9CF7)),
child: const CircularProgressIndicator(strokeWidth: 2.2, color: AppTheme.primary),
),
const SizedBox(width: 10),
Text(thinkingText?.isNotEmpty == true ? thinkingText! : '正在分析...', style: const TextStyle(fontSize: 14, color: Color(0xFF999999))),
Text(thinkingText?.isNotEmpty == true ? thinkingText! : '正在分析...', style: const TextStyle(fontSize: 17, color: Color(0xFF999999))),
],
),
),
@@ -866,7 +867,7 @@ class ChatMessagesView extends ConsumerWidget {
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.82),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: isUser ? const Color(0xFF8B9CF7) : const Color(0xFFFEFEFF),
color: isUser ? AppTheme.primary : const Color(0xFFFEFEFF),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(isUser ? 20 : 4),
topRight: Radius.circular(isUser ? 4 : 20),
@@ -874,23 +875,23 @@ class ChatMessagesView extends ConsumerWidget {
bottomRight: const Radius.circular(20),
),
border: isUser ? null : Border.all(color: const Color(0xFFD8DCFD), width: 1.5),
boxShadow: isUser ? [] : [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
boxShadow: isUser ? [] : [BoxShadow(color: AppTheme.primary.withAlpha(12), blurRadius: 10, offset: const Offset(0, 3))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 文字内容
if (isUser)
SelectableText(msg.content, style: const TextStyle(fontSize: 16, color: Colors.white, height: 1.4))
SelectableText(msg.content, style: const TextStyle(fontSize: 19, color: Colors.white, height: 1.4))
else
MarkdownBody(
data: msg.content,
selectable: true,
styleSheet: MarkdownStyleSheet(
p: const TextStyle(fontSize: 16, color: Color(0xFF1A1A1A), height: 1.5),
h1: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
h2: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
code: const TextStyle(fontSize: 14, backgroundColor: Color(0xFFF0F2FF)),
p: const TextStyle(fontSize: 19, color: Color(0xFF1A1A1A), height: 1.5),
h1: const TextStyle(fontSize: 21, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
h2: const TextStyle(fontSize: 19, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A)),
code: const TextStyle(fontSize: 17, backgroundColor: AppTheme.primaryLight),
),
),
@@ -910,7 +911,7 @@ class ChatMessagesView extends ConsumerWidget {
? Image.network(imageUrl, fit: BoxFit.cover, errorBuilder: (_, e, s) => Container(
width: 80, height: 60,
decoration: BoxDecoration(color: const Color(0xFFF0F0F0), borderRadius: BorderRadius.circular(8)),
child: const Icon(Icons.image, size: 24, color: Color(0xFFBBBBBB)),
child: const Icon(Icons.image, size: 28, color: Color(0xFFBBBBBB)),
))
: const SizedBox.shrink(),
),
@@ -922,11 +923,11 @@ class ChatMessagesView extends ConsumerWidget {
Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(children: [
const CircleAvatar(radius: 10, backgroundColor: Color(0xFFF0F2FF), child: Icon(Icons.chat_bubble_outline, size: 14, color: Color(0xFF8B9CF7))),
const CircleAvatar(radius: 10, backgroundColor: AppTheme.primaryLight, child: Icon(Icons.chat_bubble_outline, size: 17, color: AppTheme.primary)),
const SizedBox(width: 6),
const Text('健康管家', style: TextStyle(fontSize: 12, color: Color(0xFF9E9E9E))),
const Text('健康管家', style: TextStyle(fontSize: 15, color: Color(0xFF9E9E9E))),
const SizedBox(width: 4),
const Text('仅供参考', style: TextStyle(fontSize: 11, color: Color(0xFFCCCCCC))),
const Text('仅供参考', style: TextStyle(fontSize: 14, color: Color(0xFFCCCCCC))),
]),
),
],
@@ -943,16 +944,16 @@ class ChatMessagesView extends ConsumerWidget {
return ElevatedButton(
onPressed: onTap ?? () {},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6C5CE7),
backgroundColor: AppTheme.primary,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 11),
),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Icon(icon, size: 16),
Icon(icon, size: 19),
const SizedBox(width: 5),
Text(label, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500)),
Text(label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500)),
]),
);
}
@@ -961,15 +962,15 @@ class ChatMessagesView extends ConsumerWidget {
return OutlinedButton(
onPressed: onTap ?? () {},
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF6C5CE7),
side: const BorderSide(color: Color(0xFF6C5CE7), width: 1.2),
foregroundColor: AppTheme.primary,
side: const BorderSide(color: AppTheme.primary, width: 1.2),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 11),
),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Icon(icon, size: 15),
Icon(icon, size: 18),
const SizedBox(width: 4),
Text(label, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500)),
Text(label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500)),
]),
);
}
@@ -991,7 +992,7 @@ class ChatMessagesView extends ConsumerWidget {
: Image.file(File(path), fit: BoxFit.contain))),
Positioned(top: 8, right: 8, child: GestureDetector(
onTap: () => Navigator.pop(ctx),
child: Container(padding: const EdgeInsets.all(6), decoration: BoxDecoration(color: Colors.white54, shape: BoxShape.circle), child: const Icon(Icons.close, size: 18)),
child: Container(padding: const EdgeInsets.all(6), decoration: BoxDecoration(color: Colors.white54, shape: BoxShape.circle), child: const Icon(Icons.close, size: 21)),
)),
]),
),
@@ -1069,7 +1070,7 @@ class ChatMessagesView extends ConsumerWidget {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('打卡成功 已记录 ${reminders.length} 项服药'),
backgroundColor: const Color(0xFF43A047),
backgroundColor: AppTheme.success,
),
);
} catch (e) {
@@ -1260,13 +1261,13 @@ class ChatMessagesView extends ConsumerWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [BoxShadow(color: const Color(0xFF8B9CF7).withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))],
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(10), blurRadius: 8, offset: const Offset(0, 2))],
),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
Icon(Icons.today, size: 18, color: const Color(0xFF8B9CF7)),
Icon(Icons.today, size: 21, color: AppTheme.primary),
const SizedBox(width: 8),
const Text('今日任务', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
const Text('今日任务', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF1A1A1A))),
]),
const SizedBox(height: 10),
...tasks,
@@ -1275,7 +1276,7 @@ class ChatMessagesView extends ConsumerWidget {
}
Widget _taskRow(BuildContext context, IconData icon, String label, {String status = 'pending', String? trailing, VoidCallback? onTap}) {
final colors = {'done': const Color(0xFF43A047), 'warning': const Color(0xFFFF9800), 'pending': const Color(0xFF9E9E9E), 'overdue': const Color(0xFFE53935)};
final colors = {'done': AppTheme.success, 'warning': const Color(0xFFFF9800), 'pending': const Color(0xFF9E9E9E), 'overdue': AppTheme.error};
final icons = {'done': Icons.check_circle, 'warning': Icons.warning, 'pending': Icons.circle_outlined, 'overdue': Icons.error};
final isOverdue = status == 'overdue';
return Padding(
@@ -1290,10 +1291,10 @@ class ChatMessagesView extends ConsumerWidget {
borderRadius: BorderRadius.circular(8),
),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Container(width: 30, height: 30, decoration: BoxDecoration(color: const Color(0xFFF0F2FF), borderRadius: BorderRadius.circular(8)), child: Icon(icon, size: 15, color: const Color(0xFF8B9CF7))),
Container(width: 30, height: 30, decoration: BoxDecoration(color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(8)), child: Icon(icon, size: 18, color: AppTheme.primary)),
const SizedBox(width: 10),
Expanded(child: Text(trailing ?? label, style: const TextStyle(fontSize: 13, color: Color(0xFF333333)))),
Icon(icons[status] ?? Icons.circle_outlined, size: 18, color: colors[status] ?? Colors.grey),
Expanded(child: Text(trailing ?? label, style: const TextStyle(fontSize: 16, color: Color(0xFF333333)))),
Icon(icons[status] ?? Icons.circle_outlined, size: 21, color: colors[status] ?? Colors.grey),
]),
),
),
@@ -1391,16 +1392,16 @@ class _ExpandableAdviceState extends State<_ExpandableAdvice> {
children: [
Row(
children: [
const Icon(Icons.lightbulb_outline, size: 16, color: Color(0xFF8B9CF7)),
const Icon(Icons.lightbulb_outline, size: 19, color: AppTheme.primary),
const SizedBox(width: 6),
const Text('AI 建议', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Color(0xFF8B9CF7))),
const Text('AI 建议', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.primary)),
const Spacer(),
Icon(_expanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, size: 18, color: const Color(0xFFAAAAAA)),
Icon(_expanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, size: 21, color: const Color(0xFFAAAAAA)),
],
),
if (_expanded) ...[
const SizedBox(height: 10),
Text(widget.advice, style: const TextStyle(fontSize: 13, color: Color(0xFF555555), height: 1.6)),
Text(widget.advice, style: const TextStyle(fontSize: 16, color: Color(0xFF555555), height: 1.6)),
],
],
),