Files
jiu/client/lib/core/theme/app_theme.dart
T
wangjia 9d5bc18de1
Deploy Client / build-client-web (push) Successful in 38s
Deploy Client / build-macos (push) Successful in 2m10s
Deploy Client / build-android (push) Successful in 56s
Deploy Client / build-ios (push) Successful in 2m35s
Deploy Client / build-windows (push) Successful in 1m49s
Deploy Client / release-deploy-client (push) Successful in 1m23s
chore: release client-v1.0.70
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
2026-06-21 19:46:49 +08:00

289 lines
14 KiB
Dart

import 'package:flutter/material.dart';
class AppTheme {
// ─────────────────────────────────────────────────────────────────────────
// LEGACY API — field names preserved, values updated to 岩美 brand.
// ─────────────────────────────────────────────────────────────────────────
static const Color primary = Color(0xFF2563AC);
static const Color primaryDark = Color(0xFF154072);
static const Color primaryLight = Color(0xFF4F86C6);
/// Bordeaux accent — wine context cue. Was warm orange #FF6F00.
static const Color accent = Color(0xFF8B2331);
static const Color success = Color(0xFF2E8B57);
static const Color danger = Color(0xFFD14343);
/// 警示色(amber):用于「撤回」等谨慎但非破坏性的操作,文字在白底对比足够。
static const Color warning = Color(0xFFB45309);
static const Color background = Color(0xFFF5F7FA);
static const Color surface = Color(0xFFFFFFFF);
static const Color border = Color(0xFFDCE2EB);
static const Color textPrimary = Color(0xFF232934);
static const Color textSecondary = Color(0xFF6E7888);
// ─────────────────────────────────────────────────────────────────────────
// Full brand scale (50..900)
// ─────────────────────────────────────────────────────────────────────────
static const Color brand50 = Color(0xFFEEF4FB);
static const Color brand100 = Color(0xFFD6E5F5);
static const Color brand200 = Color(0xFFADC9EA);
static const Color brand300 = Color(0xFF7FA8DA);
static const Color brand400 = Color(0xFF4F86C6);
static const Color brand500 = Color(0xFF2563AC);
static const Color brand600 = Color(0xFF1B4F8E);
static const Color brand700 = Color(0xFF154072);
static const Color brand800 = Color(0xFF0F3057);
static const Color brand900 = Color(0xFF0A1F3B);
// ─────────────────────────────────────────────────────────────────────────
// Full neutral (gray) scale
// ─────────────────────────────────────────────────────────────────────────
static const Color gray0 = Color(0xFFFFFFFF);
static const Color gray25 = Color(0xFFFBFCFD);
static const Color gray50 = Color(0xFFF5F7FA);
static const Color gray100 = Color(0xFFECEFF4);
static const Color gray200 = Color(0xFFDCE2EB);
static const Color gray300 = Color(0xFFC2CAD6);
static const Color gray400 = Color(0xFF99A3B3);
static const Color gray500 = Color(0xFF6E7888);
static const Color gray600 = Color(0xFF4F5867);
static const Color gray700 = Color(0xFF353C48);
static const Color gray800 = Color(0xFF232934);
static const Color gray900 = Color(0xFF141821);
// ─────────────────────────────────────────────────────────────────────────
// Accent (bordeaux) scale
// ─────────────────────────────────────────────────────────────────────────
static const Color accent50 = Color(0xFFFAEEF0);
static const Color accent100 = Color(0xFFF1D2D7);
static const Color accent500 = Color(0xFF8B2331);
static const Color accent700 = Color(0xFF5F1621);
// ─────────────────────────────────────────────────────────────────────────
// Semantic scale
// ─────────────────────────────────────────────────────────────────────────
static const Color success50 = Color(0xFFE8F5EE);
static const Color success500 = Color(0xFF2E8B57);
static const Color success700 = Color(0xFF1F6B41);
static const Color warning50 = Color(0xFFFFF4DB);
static const Color warning500 = Color(0xFFE08E00);
static const Color warning700 = Color(0xFFA66700);
static const Color danger50 = Color(0xFFFDECEC);
static const Color danger500 = Color(0xFFD14343);
static const Color danger700 = Color(0xFF9E2A2A);
static const Color info50 = Color(0xFFE5F1FB);
static const Color info500 = Color(0xFF2F7BD0);
static const Color info700 = Color(0xFF1F5C9F);
// ─────────────────────────────────────────────────────────────────────────
// State colors
// ─────────────────────────────────────────────────────────────────────────
static const Color rowHover = Color(0xFFEEF4FB);
static const Color tableHeader = Color(0xFFF5F7FA);
static const Color borderSubtle = Color(0xFFECEFF4);
// ─────────────────────────────────────────────────────────────────────────
// Radii
// ─────────────────────────────────────────────────────────────────────────
static const double radiusSm = 4.0;
static const double radiusMd = 6.0;
static const double radiusLg = 10.0;
// ─────────────────────────────────────────────────────────────────────────
// Spacing (4px base)
// ─────────────────────────────────────────────────────────────────────────
static const double space1 = 4.0;
static const double space2 = 8.0;
static const double space3 = 12.0;
static const double space4 = 16.0;
static const double space5 = 20.0;
static const double space6 = 24.0;
static const double space8 = 32.0;
// ─────────────────────────────────────────────────────────────────────────
// ThemeData
// ─────────────────────────────────────────────────────────────────────────
static ThemeData light() {
return ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primary,
brightness: Brightness.light,
).copyWith(
primary: primary,
onPrimary: Colors.white,
secondary: brand400,
surface: surface,
onSurface: textPrimary,
error: danger,
outline: border,
outlineVariant: borderSubtle,
),
scaffoldBackgroundColor: background,
appBarTheme: const AppBarTheme(
backgroundColor: primary,
foregroundColor: Colors.white,
elevation: 0,
centerTitle: false,
toolbarHeight: 56,
titleTextStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 0.4,
),
),
cardTheme: CardThemeData(
color: surface,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusLg),
side: const BorderSide(color: border, width: 1),
),
margin: const EdgeInsets.all(0),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primary,
foregroundColor: Colors.white,
minimumSize: const Size(0, 40),
padding: const EdgeInsets.symmetric(horizontal: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
textStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
letterSpacing: 0.2,
),
elevation: 0,
).copyWith(
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.pressed)) return primaryDark;
if (states.contains(WidgetState.hovered)) return brand600;
return primary;
}),
overlayColor:
WidgetStateProperty.all(Colors.white.withValues(alpha: 0.08)),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: brand700,
minimumSize: const Size(0, 40),
padding: const EdgeInsets.symmetric(horizontal: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusMd),
),
side: const BorderSide(color: border, width: 1),
textStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
).copyWith(
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.pressed)) return gray100;
if (states.contains(WidgetState.hovered)) return gray50;
return surface;
}),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: primary,
minimumSize: const Size(0, 36),
padding: const EdgeInsets.symmetric(horizontal: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radiusSm),
),
),
),
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: const BorderSide(color: border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: const BorderSide(color: border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: const BorderSide(color: primary, width: 1.5),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(radiusMd),
borderSide: const BorderSide(color: danger),
),
hoverColor: gray50,
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
isDense: true,
filled: true,
fillColor: surface,
labelStyle: const TextStyle(fontSize: 13, color: textSecondary),
hintStyle: TextStyle(fontSize: 13, color: gray400),
),
dividerTheme: const DividerThemeData(
color: borderSubtle,
thickness: 1,
space: 1,
),
chipTheme: ChipThemeData(
backgroundColor: gray100,
labelStyle: const TextStyle(fontSize: 12, color: textPrimary),
side: BorderSide.none,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(999),
),
),
tooltipTheme: TooltipThemeData(
decoration: BoxDecoration(
color: gray900,
borderRadius: BorderRadius.circular(radiusSm),
),
textStyle: const TextStyle(color: Colors.white, fontSize: 12),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
),
textTheme: const TextTheme(
displayLarge: TextStyle(fontSize: 36, fontWeight: FontWeight.w600, color: brand900, height: 1.2, letterSpacing: 0.4),
displayMedium: TextStyle(fontSize: 28, fontWeight: FontWeight.w600, color: brand900, height: 1.2, letterSpacing: 0.4),
headlineSmall: TextStyle(fontSize: 22, fontWeight: FontWeight.w600, color: gray900, height: 1.3),
titleLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: gray900, height: 1.35),
titleMedium: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: textPrimary, height: 1.4),
titleSmall: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: textPrimary, height: 1.4),
bodyLarge: TextStyle(fontSize: 14, color: textPrimary, height: 1.55),
bodyMedium: TextStyle(fontSize: 14, color: textPrimary, height: 1.55),
bodySmall: TextStyle(fontSize: 12, color: textSecondary, height: 1.5),
labelLarge: TextStyle(fontSize: 13, color: textPrimary, fontWeight: FontWeight.w500),
labelMedium: TextStyle(fontSize: 12, color: textSecondary, letterSpacing: 0.4),
labelSmall: TextStyle(fontSize: 11, color: textSecondary, letterSpacing: 0.4),
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.windows: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.macOS: FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.linux: FadeUpwardsPageTransitionsBuilder(),
},
),
);
}
}