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
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YZ4DskSRKsSiheQonFtQvx
289 lines
14 KiB
Dart
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(),
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|