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

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../core/app_theme.dart';
/// 统一菜单项profile_page 和 settings_pages 都共用这个
class AppMenuItem extends StatelessWidget {
final IconData icon;
final String title;
final String? subtitle;
final String? trailing;
final VoidCallback? onTap;
const AppMenuItem({
super.key,
required this.icon,
required this.title,
this.subtitle,
this.trailing,
this.onTap,
});
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 2),
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 14),
decoration: BoxDecoration(
color: theme.colorScheme.card,
borderRadius: BorderRadius.circular(AppTheme.rSm),
),
child: Row(children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: AppTheme.primaryLight,
borderRadius: BorderRadius.circular(AppTheme.rSm),
),
child: Icon(icon, size: 22, color: AppTheme.primary),
),
const SizedBox(width: AppTheme.sLg),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500, color: theme.colorScheme.foreground)),
if (subtitle != null && subtitle!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(subtitle!, style: TextStyle(fontSize: 16, color: theme.colorScheme.mutedForeground)),
),
],
),
),
if (trailing != null && trailing!.isNotEmpty)
Text(trailing!, style: TextStyle(fontSize: 17, color: theme.colorScheme.mutedForeground)),
const SizedBox(width: 4),
Icon(LucideIcons.chevronRight, size: 21, color: theme.colorScheme.mutedForeground),
]),
),
);
}
}