feat: 蓝牙血压计基础架构 + UI配色体系升级
- 新增 AppColors 配色体系(紫蓝渐变 + 超大圆角 + 漂浮卡片) - 新增 OmronBleService(BLE 扫描/连接/SFLOAT 协议解析) - 新增 BpReading 数据模型 + 血压数据自动同步后端 - 新增 DeviceScanPage(血压计扫描绑定页)+ DeviceBindPage(已绑定管理页) - 侧边栏新增"蓝牙设备"入口,ProfilePage 移除设备管理 - Android/iOS BLE 权限配置(BLUETOOTH_SCAN/CONNECT) - AppTheme 升级:新阴影系统、新语义色、圆角放大
This commit is contained in:
@@ -2,65 +2,84 @@ import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'app_colors.dart';
|
||||
|
||||
/// 健康管家 — 清爽紫蓝风
|
||||
/// 健康管家 —— 蚂蚁阿福风格:紫蓝渐变 + 超大圆角 + 漂浮卡片
|
||||
class AppTheme {
|
||||
AppTheme._();
|
||||
|
||||
// ── 主色调 ──
|
||||
static const Color primary = AppColors.primaryPurple;
|
||||
static const Color primaryLight = AppColors.iconBackground;
|
||||
static const Color primaryDark = AppColors.iconColor;
|
||||
static const Color primaryGlow = AppColors.purpleGradientStart;
|
||||
static const Color primary = AppColors.primary;
|
||||
static const Color primaryLight = AppColors.primaryLight;
|
||||
static const Color primaryDark = AppColors.primaryDark;
|
||||
static const Color primaryMid = AppColors.primaryMid;
|
||||
static const Color accent = AppColors.accentCyan;
|
||||
static const Color info = AppColors.accentCyan;
|
||||
|
||||
// ── 中性色 ──
|
||||
static const Color bg = AppColors.backgroundSecondary;
|
||||
static const Color surface = AppColors.cardBackground;
|
||||
static const Color text = AppColors.textPrimary;
|
||||
static const Color textSub = AppColors.textSecondary;
|
||||
static const Color bg = AppColors.background;
|
||||
static const Color bgSoft = AppColors.backgroundSoft;
|
||||
static const Color surface = AppColors.cardBackground;
|
||||
static const Color surfaceInner = AppColors.cardInner;
|
||||
static const Color text = AppColors.textPrimary;
|
||||
static const Color textSub = AppColors.textSecondary;
|
||||
static const Color textHint = AppColors.textHint;
|
||||
static const Color border = AppColors.border;
|
||||
static const Color divider = AppColors.borderLight;
|
||||
static const Color border = AppColors.border;
|
||||
static const Color divider = AppColors.borderLight;
|
||||
|
||||
// ── 语义色 ──
|
||||
static const Color success = AppColors.success;
|
||||
static const Color error = AppColors.error;
|
||||
static const Color successLight = AppColors.successLight;
|
||||
static const Color error = AppColors.error;
|
||||
static const Color errorLight = AppColors.errorLight;
|
||||
static const Color warning = AppColors.warning;
|
||||
static const Color accent = AppColors.primaryBlue;
|
||||
static const Color info = AppColors.primaryBlue;
|
||||
static const Color warningLight = AppColors.warningLight;
|
||||
|
||||
// ── 圆角 ──
|
||||
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 rXs = 8;
|
||||
static const double rSm = 12;
|
||||
static const double rMd = 18;
|
||||
static const double rLg = 24;
|
||||
static const double rXl = 32;
|
||||
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 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 BoxShadow get shadowCard => BoxShadow(
|
||||
color: AppColors.primary.withOpacity(0.08),
|
||||
blurRadius: 24,
|
||||
offset: const Offset(0, 8),
|
||||
);
|
||||
static BoxShadow get shadowLight => BoxShadow(
|
||||
color: AppColors.primary.withOpacity(0.05),
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 4),
|
||||
);
|
||||
static BoxShadow get shadowElevated => BoxShadow(
|
||||
color: AppColors.primary.withOpacity(0.15),
|
||||
blurRadius: 32,
|
||||
offset: const Offset(0, 10),
|
||||
);
|
||||
static BoxShadow get shadowButton => BoxShadow(
|
||||
color: AppColors.primary.withOpacity(0.25),
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 6),
|
||||
);
|
||||
|
||||
// ── 智能体配色 ──
|
||||
static const Map<String, Color> agentColors = {
|
||||
'default': AppColors.iconBackground,
|
||||
'consultation': Color(0xFFE0F0FF),
|
||||
'health': Color(0xFFE8FFE8),
|
||||
'diet': Color(0xFFFFF0E0),
|
||||
'medication': Color(0xFFFFE8EC),
|
||||
'report': Color(0xFFE8F0FF),
|
||||
'exercise': Color(0xFFF0E8FF),
|
||||
'default': AppColors.primaryLight,
|
||||
'consultation': Color(0xFFE0E7FF),
|
||||
'health': Color(0xFFDCFCE7),
|
||||
'diet': Color(0xFFFEF3C7),
|
||||
'medication': Color(0xFFFCE7F3),
|
||||
'report': Color(0xFFDBEAFE),
|
||||
'exercise': Color(0xFFEDE9FE),
|
||||
};
|
||||
|
||||
static Color agentLight(String? name) => agentColors[name] ?? primaryLight;
|
||||
@@ -71,19 +90,26 @@ class AppTheme {
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: primary,
|
||||
primary: primary,
|
||||
surface: bg,
|
||||
primaryContainer: primaryLight,
|
||||
onPrimary: Colors.white,
|
||||
secondary: accent,
|
||||
surface: surface,
|
||||
background: bg,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
scaffoldBackgroundColor: bg,
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
hoverColor: primaryLight.withOpacity(0.5),
|
||||
fontFamily: null,
|
||||
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: surface,
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: bg,
|
||||
foregroundColor: text,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
centerTitle: false,
|
||||
scrolledUnderElevation: 0,
|
||||
titleTextStyle: TextStyle(fontSize: 23, fontWeight: FontWeight.w600, color: text),
|
||||
titleTextStyle: TextStyle(fontSize: 22, fontWeight: FontWeight.w700, color: text),
|
||||
),
|
||||
|
||||
cardTheme: CardThemeData(
|
||||
@@ -95,7 +121,7 @@ class AppTheme {
|
||||
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: bg,
|
||||
fillColor: bgSoft,
|
||||
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),
|
||||
@@ -103,7 +129,7 @@ class AppTheme {
|
||||
borderRadius: BorderRadius.circular(rMd),
|
||||
borderSide: const BorderSide(color: primary, width: 2),
|
||||
),
|
||||
hintStyle: const TextStyle(color: textHint, fontSize: 20),
|
||||
hintStyle: TextStyle(color: textHint, fontSize: 16),
|
||||
),
|
||||
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
@@ -112,28 +138,29 @@ class AppTheme {
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(double.infinity, 56),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rPill)),
|
||||
textStyle: const TextStyle(fontSize: 21, fontWeight: FontWeight.w600),
|
||||
textStyle: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600),
|
||||
elevation: 0,
|
||||
shadowColor: primary.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
|
||||
dialogTheme: DialogThemeData(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(rXl))),
|
||||
|
||||
textTheme: const TextTheme(
|
||||
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),
|
||||
headlineLarge: TextStyle(fontSize: 28, fontWeight: FontWeight.w700, color: text, letterSpacing: -0.3),
|
||||
titleLarge: TextStyle(fontSize: 22, fontWeight: FontWeight.w700, color: text),
|
||||
titleMedium: TextStyle(fontSize: 20, fontWeight: FontWeight.w600, color: text),
|
||||
bodyLarge: TextStyle(fontSize: 16, color: text, height: 1.5),
|
||||
bodyMedium: TextStyle(fontSize: 14, color: textSub, height: 1.4),
|
||||
labelMedium: TextStyle(fontSize: 13, color: textSub),
|
||||
labelSmall: TextStyle(fontSize: 12, color: textHint),
|
||||
),
|
||||
);
|
||||
|
||||
// ── Shadcn UI 主题 ──
|
||||
// ── Shadcn UI 主题(紫蓝系) ──
|
||||
static ShadThemeData get shadTheme => ShadThemeData(
|
||||
brightness: Brightness.light,
|
||||
colorScheme: const ShadVioletColorScheme.light(
|
||||
colorScheme: ShadVioletColorScheme.light(
|
||||
background: bg,
|
||||
foreground: text,
|
||||
card: surface,
|
||||
@@ -142,9 +169,9 @@ class AppTheme {
|
||||
popoverForeground: text,
|
||||
primary: primary,
|
||||
primaryForeground: Colors.white,
|
||||
secondary: divider,
|
||||
secondaryForeground: text,
|
||||
muted: divider,
|
||||
secondary: primaryLight,
|
||||
secondaryForeground: primaryDark,
|
||||
muted: bgSoft,
|
||||
mutedForeground: textSub,
|
||||
accent: accent,
|
||||
accentForeground: Colors.white,
|
||||
@@ -153,7 +180,7 @@ class AppTheme {
|
||||
border: border,
|
||||
input: border,
|
||||
ring: primary,
|
||||
selection: primaryGlow,
|
||||
selection: primaryLight,
|
||||
),
|
||||
radius: BorderRadius.circular(rMd),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user