427 lines
14 KiB
Dart
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)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|