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:
@@ -1,97 +1,160 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
/// 健康管家 — Lavender Breeze 淡紫清风
|
||||
/// 健康管家 —「朝露」Morning Dew
|
||||
/// 深青绿 + 薄荷白,植物系清新风格
|
||||
class AppTheme {
|
||||
AppTheme._();
|
||||
|
||||
// ── 颜色 ──
|
||||
static const Color primary = Color(0xFF6C5CE7);
|
||||
static const Color primaryLight = Color(0xFFEDEAFF);
|
||||
static const Color primaryDark = Color(0xFF5A4BD1);
|
||||
static const Color bg = Color(0xFFF8F9FC);
|
||||
static const Color surface = Color(0xFFFFFFFF);
|
||||
static const Color text = Color(0xFF2D2B32);
|
||||
static const Color textSub = Color(0xFF8A8892);
|
||||
static const Color textHint = Color(0xFFBFBCC4);
|
||||
static const Color success = Color(0xFF6ECF8A);
|
||||
static const Color error = Color(0xFFF56C6C);
|
||||
static const Color warning = Color(0xFFF5A623);
|
||||
static const Color accent = Color(0xFFFF8068);
|
||||
static const Color border = Color(0xFFEAEAF0);
|
||||
static const Color divider = Color(0xFFF2F2F6);
|
||||
// ── 主色调:深青绿 ──
|
||||
static const Color primary = Color(0xFF2D6A4F); // 深青绿
|
||||
static const Color primaryLight = Color(0xFFD8F3DC); // 浅薄荷
|
||||
static const Color primaryDark = Color(0xFF1B4332); // 深森林
|
||||
static const Color primaryGlow = Color(0xFFB7E4C7); // 发光薄荷
|
||||
|
||||
// ── 圆角 Token ──
|
||||
static const double radiusXs = 8;
|
||||
static const double radiusSm = 12;
|
||||
static const double radiusMd = 16;
|
||||
static const double radiusLg = 20;
|
||||
static const double radiusXl = 24;
|
||||
static const double radiusPill = 999;
|
||||
// ── 中性色 ──
|
||||
static const Color bg = Color(0xFFF7FAF8); // 暖白底
|
||||
static const Color surface = Color(0xFFFFFFFF);
|
||||
static const Color text = Color(0xFF1B281F); // 深绿黑
|
||||
static const Color textSub = Color(0xFF6B7B6F); // 灰绿
|
||||
static const Color textHint = Color(0xFFB7BFB9); // 浅灰绿
|
||||
static const Color border = Color(0xFFE0E8E2);
|
||||
static const Color divider = Color(0xFFF0F4F1);
|
||||
|
||||
// ── 间距 Token ──
|
||||
static const double spaceXs = 4;
|
||||
static const double spaceSm = 8;
|
||||
static const double spaceMd = 12;
|
||||
static const double spaceLg = 16;
|
||||
static const double spaceXl = 20;
|
||||
// ── 语义色 ──
|
||||
static const Color success = Color(0xFF40916C); // 翠绿
|
||||
static const Color error = Color(0xFFE07A5F); // 陶土红
|
||||
static const Color warning = Color(0xFFF2CC8F); // 蜂蜜黄
|
||||
static const Color accent = Color(0xFF81B29A); // 中青绿
|
||||
static const Color info = Color(0xFF52B788); // 亮绿
|
||||
|
||||
// ── 阴影 Token ──
|
||||
static BoxShadow get shadowCard => BoxShadow(color: primary.withAlpha(15), blurRadius: 12, offset: const Offset(0, 4));
|
||||
static BoxShadow get shadowLight => BoxShadow(color: primary.withAlpha(10), blurRadius: 8, offset: const Offset(0, 2));
|
||||
// ── 圆角 ──
|
||||
static const double rXs = 6;
|
||||
static const double rSm = 10;
|
||||
static const double rMd = 14;
|
||||
static const double rLg = 20;
|
||||
static const double rXl = 28;
|
||||
static const double rPill = 999;
|
||||
|
||||
/// 每个智能体的卡片色调
|
||||
// ── 间距 ──
|
||||
static const double sXs = 4;
|
||||
static const double sSm = 8;
|
||||
static const double sMd = 12;
|
||||
static const double sLg = 16;
|
||||
static const double sXl = 20;
|
||||
static const double sXxl = 28;
|
||||
|
||||
// ── 阴影 ──
|
||||
static BoxShadow get shadowCard =>
|
||||
BoxShadow(color: primary.withAlpha(14), blurRadius: 20, offset: const Offset(0, 4));
|
||||
static BoxShadow get shadowLight =>
|
||||
BoxShadow(color: primary.withAlpha(8), blurRadius: 12, offset: const Offset(0, 2));
|
||||
static BoxShadow get shadowElevated =>
|
||||
BoxShadow(color: primary.withAlpha(20), blurRadius: 30, offset: const Offset(0, 8));
|
||||
|
||||
// ── 智能体配色 ──
|
||||
static const Map<String, Color> agentColors = {
|
||||
'default': Color(0xFFE8ECFF), // 淡蓝紫
|
||||
'consultation': Color(0xFFE8F5FF), // 淡天蓝
|
||||
'health': Color(0xFFE8FFF0), // 淡薄荷
|
||||
'diet': Color(0xFFFFF2E8), // 淡杏
|
||||
'medication': Color(0xFFFFE8F0), // 淡粉
|
||||
'report': Color(0xFFE8F4FF), // 淡水蓝
|
||||
'default': Color(0xFFD8F3DC), // 薄荷
|
||||
'consultation': Color(0xFFE0F0FF), // 淡天蓝
|
||||
'health': Color(0xFFE8FFE8), // 淡翠绿
|
||||
'diet': Color(0xFFFFF0E0), // 淡杏
|
||||
'medication': Color(0xFFFFE8EC), // 淡粉
|
||||
'report': Color(0xFFE8F0FF), // 淡水蓝
|
||||
'exercise': Color(0xFFF0E8FF), // 淡紫
|
||||
};
|
||||
|
||||
static Color agentLight(String? name) => agentColors[name] ?? primaryLight;
|
||||
|
||||
// ── Material 主题 ──
|
||||
static ThemeData get lightTheme => ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: primary, primary: primary, surface: bg, brightness: Brightness.light),
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: primary,
|
||||
primary: primary,
|
||||
surface: bg,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
scaffoldBackgroundColor: bg,
|
||||
fontFamily: null, // 使用系统默认字体
|
||||
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: surface, foregroundColor: text, elevation: 0,
|
||||
centerTitle: true, scrolledUnderElevation: 0,
|
||||
titleTextStyle: TextStyle(fontSize: 17, fontWeight: FontWeight.w600, color: text),
|
||||
backgroundColor: surface,
|
||||
foregroundColor: text,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
scrolledUnderElevation: 0,
|
||||
titleTextStyle: TextStyle(fontSize: 23, fontWeight: FontWeight.w600, color: text),
|
||||
),
|
||||
|
||||
cardTheme: CardThemeData(color: surface, elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(radiusXl)), margin: EdgeInsets.zero),
|
||||
cardTheme: CardThemeData(
|
||||
color: surface,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rLg)),
|
||||
margin: EdgeInsets.zero,
|
||||
),
|
||||
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true, fillColor: const Color(0xFFF4F5FA),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(radiusMd), borderSide: BorderSide.none),
|
||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(radiusMd), borderSide: BorderSide.none),
|
||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(radiusMd), borderSide: const BorderSide(color: primary, width: 1.5)),
|
||||
hintStyle: const TextStyle(color: textHint, fontSize: 15),
|
||||
filled: true,
|
||||
fillColor: bg,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: sLg, vertical: 16),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(rMd), borderSide: BorderSide.none),
|
||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(rMd), borderSide: BorderSide.none),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(rMd),
|
||||
borderSide: const BorderSide(color: primary, width: 2),
|
||||
),
|
||||
hintStyle: const TextStyle(color: textHint, fontSize: 20),
|
||||
),
|
||||
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(style: ElevatedButton.styleFrom(
|
||||
backgroundColor: primary, foregroundColor: Colors.white,
|
||||
minimumSize: const Size(double.infinity, 52),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(radiusLg)),
|
||||
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), elevation: 0,
|
||||
)),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: primary,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(double.infinity, 56),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rPill)),
|
||||
textStyle: const TextStyle(fontSize: 21, fontWeight: FontWeight.w600),
|
||||
elevation: 0,
|
||||
),
|
||||
),
|
||||
|
||||
dialogTheme: DialogThemeData(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(radiusXl))),
|
||||
dialogTheme: DialogThemeData(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rXl))),
|
||||
|
||||
textTheme: const TextTheme(
|
||||
headlineLarge: TextStyle(fontSize: 24, fontWeight: FontWeight.w700, color: text),
|
||||
titleLarge: TextStyle(fontSize: 20, fontWeight: FontWeight.w600, color: text),
|
||||
titleMedium: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: text),
|
||||
bodyLarge: TextStyle(fontSize: 16, color: text, height: 1.5),
|
||||
bodyMedium: TextStyle(fontSize: 15, color: textSub, height: 1.4),
|
||||
labelMedium: TextStyle(fontSize: 13, color: textSub),
|
||||
labelSmall: TextStyle(fontSize: 11, color: textHint),
|
||||
headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.w700, color: text, letterSpacing: -0.5),
|
||||
titleLarge: TextStyle(fontSize: 23, fontWeight: FontWeight.w600, color: text),
|
||||
titleMedium: TextStyle(fontSize: 21, fontWeight: FontWeight.w600, color: text),
|
||||
bodyLarge: TextStyle(fontSize: 21, color: text, height: 1.5),
|
||||
bodyMedium: TextStyle(fontSize: 20, color: textSub, height: 1.4),
|
||||
labelMedium: TextStyle(fontSize: 18, color: textSub),
|
||||
labelSmall: TextStyle(fontSize: 16, color: textHint),
|
||||
),
|
||||
);
|
||||
|
||||
// ── Shadcn UI 主题 ──
|
||||
static ShadThemeData get shadTheme => ShadThemeData(
|
||||
brightness: Brightness.light,
|
||||
colorScheme: const ShadVioletColorScheme.light(
|
||||
background: bg,
|
||||
foreground: text,
|
||||
card: surface,
|
||||
cardForeground: text,
|
||||
popover: surface,
|
||||
popoverForeground: text,
|
||||
primary: primary,
|
||||
primaryForeground: Colors.white,
|
||||
secondary: Color(0xFFF0F4F1),
|
||||
secondaryForeground: text,
|
||||
muted: Color(0xFFF0F4F1),
|
||||
mutedForeground: textSub,
|
||||
accent: accent,
|
||||
accentForeground: Colors.white,
|
||||
destructive: error,
|
||||
destructiveForeground: Colors.white,
|
||||
border: border,
|
||||
input: border,
|
||||
ring: primary,
|
||||
selection: Color(0xFFB7E4C7),
|
||||
),
|
||||
radius: BorderRadius.circular(rMd),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user