Files
impact/lib/core/theme/app_theme.dart
2026-08-27 18:32:59 +02:00

427 lines
14 KiB
Dart

import 'package:flutter/material.dart';
/// Définition des couleurs d'accent disponibles pour la personnalisation.
class AppAccentColor {
final String id;
final String name;
final Color color;
final Color lightColor;
final Color darkColor;
final IconData icon;
const AppAccentColor({
required this.id,
required this.name,
required this.color,
required this.lightColor,
required this.darkColor,
required this.icon,
});
static const AppAccentColor blue = AppAccentColor(
id: 'blue',
name: 'Bleu Cobalt',
color: Color(0xFF2563EB),
lightColor: Color(0xFF60A5FA),
darkColor: Color(0xFF1D4ED8),
icon: Icons.lens,
);
static const AppAccentColor red = AppAccentColor(
id: 'red',
name: 'Rouge Cible',
color: Color(0xFFDC2626),
lightColor: Color(0xFFF87171),
darkColor: Color(0xFFB91C1C),
icon: Icons.lens,
);
static const AppAccentColor green = AppAccentColor(
id: 'green',
name: 'Vert Viseur',
color: Color(0xFF10B981),
lightColor: Color(0xFF34D399),
darkColor: Color(0xFF059669),
icon: Icons.lens,
);
static const AppAccentColor orange = AppAccentColor(
id: 'orange',
name: 'Orange Ambre',
color: Color(0xFFFF6D00),
lightColor: Color(0xFFFF9E40),
darkColor: Color(0xFFD84315),
icon: Icons.lens,
);
static const AppAccentColor purple = AppAccentColor(
id: 'purple',
name: 'Violet Cyber',
color: Color(0xFF8B5CF6),
lightColor: Color(0xFFA78BFA),
darkColor: Color(0xFF6D28D9),
icon: Icons.lens,
);
static const AppAccentColor cyan = AppAccentColor(
id: 'cyan',
name: 'Cyan Néon',
color: Color(0xFF06B6D4),
lightColor: Color(0xFF67E8F9),
darkColor: Color(0xFF0E7490),
icon: Icons.lens,
);
static const AppAccentColor gold = AppAccentColor(
id: 'gold',
name: 'Or Compétition',
color: Color(0xFFF59E0B),
lightColor: Color(0xFFFBBF24),
darkColor: Color(0xFFD97706),
icon: Icons.lens,
);
static const List<AppAccentColor> allAccents = [
blue,
red,
green,
orange,
purple,
cyan,
gold,
];
static AppAccentColor fromId(String? id) {
return allAccents.firstWhere(
(a) => a.id == id,
orElse: () => blue,
);
}
}
/// Système de design et thème de l'application Bully.
class AppTheme {
AppTheme._();
// Accents par défaut (compatibilité statique)
static const Color primaryColor = Color(0xFF2563EB);
static const Color primaryLight = Color(0xFF60A5FA);
static const Color primaryDark = Color(0xFF1D4ED8);
static const Color secondaryColor = Color(0xFF10B981);
static const Color secondaryDark = Color(0xFF059669);
static const Color accentBlue = Color(0xFF0EA5E9);
static const Color accentGold = Color(0xFFFFB300);
static const Color errorColor = Color(0xFFEF4444);
static const Color warningColor = Color(0xFFF59E0B);
static const Color successColor = Color(0xFF10B981);
// Palette Thème Sombre (Standard Stand de Tir)
static const Color darkBackground = Color(0xFF0D1219);
static const Color darkSurface = Color(0xFF161E28);
static const Color darkSurfaceElevated = Color(0xFF1E2836);
static const Color darkSurfaceVariant = Color(0xFF253243);
static const Color darkBorder = Color(0xFF2C3B4E);
static const Color darkBorderLight = Color(0xFF3B4D65);
static const Color darkTextPrimary = Color(0xFFF1F5F9);
static const Color darkTextSecondary = Color(0xFF94A3B8);
static const Color darkTextMuted = Color(0xFF64748B);
// Palette Thème Clair
static const Color lightBackground = Color(0xFFF8FAFC);
static const Color lightSurface = Color(0xFFFFFFFF);
static const Color lightSurfaceElevated = Color(0xFFFFFFFF);
static const Color lightSurfaceVariant = Color(0xFFF1F5F9);
static const Color lightBorder = Color(0xFFE2E8F0);
static const Color lightTextPrimary = Color(0xFF0F172A);
static const Color lightTextSecondary = Color(0xFF64748B);
static const Color lightTextMuted = Color(0xFF94A3B8);
// Compatibilité ascendante
static const Color backgroundColor = lightBackground;
static const Color surfaceColor = lightSurface;
static const Color textPrimary = lightTextPrimary;
static const Color textSecondary = lightTextSecondary;
// Couleurs des impacts pour l'overlay
static const Color impactColor = Color(0xFFFF3D00);
static const Color impactOutlineColor = Color(0xFFFFFFFF);
static const Color groupingCenterColor = Color(0xFF00E5FF);
static const Color groupingCircleColor = Color(0x4D00E5FF);
// Couleurs des zones de score cibles concentriques
static const List<Color> zoneColors = [
Color(0xFFFFB300), // Zone 10 - Or
Color(0xFFFFCA28), // Zone 9
Color(0xFFFF5722), // Zone 8
Color(0xFFFF7043), // Zone 7
Color(0xFF29B6F6), // Zone 6
Color(0xFF4FC3F7), // Zone 5
Color(0xFF66BB6A), // Zone 4
Color(0xFF81C784), // Zone 3
Color(0xFFFFFFFF), // Zone 2
Color(0xFFE0E0E0), // Zone 1
];
static ThemeData get lightTheme => buildLightTheme(AppAccentColor.blue);
static ThemeData get darkTheme => buildDarkTheme(AppAccentColor.blue);
static ThemeData buildLightTheme(AppAccentColor accent) {
final activePrimary = accent.color;
return ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: ColorScheme.light(
primary: activePrimary,
secondary: secondaryColor,
surface: lightSurface,
error: errorColor,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: lightTextPrimary,
onError: Colors.white,
outline: lightBorder,
),
scaffoldBackgroundColor: lightBackground,
cardColor: lightSurface,
appBarTheme: const AppBarTheme(
elevation: 0,
centerTitle: true,
backgroundColor: lightSurface,
foregroundColor: lightTextPrimary,
surfaceTintColor: Colors.transparent,
titleTextStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: lightTextPrimary,
letterSpacing: 0.3,
),
),
cardTheme: CardThemeData(
elevation: 0,
color: lightSurface,
surfaceTintColor: Colors.transparent,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: lightBorder, width: 1),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: activePrimary,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 50),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: lightTextPrimary,
minimumSize: const Size(double.infinity, 50),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
side: const BorderSide(color: lightBorder, width: 1.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
),
chipTheme: ChipThemeData(
backgroundColor: lightSurfaceVariant,
selectedColor: activePrimary.withValues(alpha: 0.15),
labelStyle: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: lightTextPrimary,
),
secondaryLabelStyle: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: activePrimary,
),
side: const BorderSide(color: lightBorder, width: 1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: lightSurfaceVariant,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: lightBorder),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: lightBorder),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: activePrimary, width: 1.8),
),
labelStyle: const TextStyle(color: lightTextSecondary),
hintStyle: const TextStyle(color: lightTextMuted),
),
dividerTheme: const DividerThemeData(
color: lightBorder,
thickness: 1,
space: 24,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: activePrimary,
foregroundColor: Colors.white,
elevation: 3,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16)),
),
),
);
}
static ThemeData buildDarkTheme(AppAccentColor accent) {
final activePrimary = accent.color;
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.dark(
primary: activePrimary,
secondary: secondaryColor,
surface: darkSurface,
error: errorColor,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: darkTextPrimary,
onError: Colors.white,
outline: darkBorder,
),
scaffoldBackgroundColor: darkBackground,
cardColor: darkSurface,
appBarTheme: const AppBarTheme(
elevation: 0,
centerTitle: true,
backgroundColor: darkBackground,
foregroundColor: darkTextPrimary,
surfaceTintColor: Colors.transparent,
titleTextStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: darkTextPrimary,
letterSpacing: 0.3,
),
),
cardTheme: CardThemeData(
elevation: 0,
color: darkSurface,
surfaceTintColor: Colors.transparent,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: darkBorder, width: 1),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: activePrimary,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 50),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: darkTextPrimary,
minimumSize: const Size(double.infinity, 50),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
side: const BorderSide(color: darkBorder, width: 1.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
),
chipTheme: ChipThemeData(
backgroundColor: darkSurfaceElevated,
selectedColor: activePrimary.withValues(alpha: 0.2),
labelStyle: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: darkTextPrimary,
),
secondaryLabelStyle: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: activePrimary,
),
side: const BorderSide(color: darkBorder, width: 1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: darkSurfaceElevated,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: darkBorder),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: darkBorder),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: activePrimary, width: 1.8),
),
labelStyle: const TextStyle(color: darkTextSecondary),
hintStyle: const TextStyle(color: darkTextMuted),
),
dividerTheme: const DividerThemeData(
color: darkBorder,
thickness: 1,
space: 24,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: activePrimary,
foregroundColor: Colors.white,
elevation: 3,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16)),
),
),
);
}
}