mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
Replaces the Projector Noir palette with "Warm Cinema" and rebuilds the web players around a shared, event-driven engine. Design - app_colors: warm charcoal (#0B0908), cream (#F3E9DF), ember (#D9541F); adds posterScrim/heroScrim gradients, lift()/emberFocus() shadows, const cream-alpha tokens replacing Colors.whiteNN, and primaryFill, a deeper ember reserved for text-bearing fills so white labels clear the 4.5:1 AA threshold that #D9541F alone does not - app_theme / mobile_theme: Fraunces (display serif) + Karla (body), tighter radii, 20+ component themes - glass_container: opaque layered surfaces replace the blurred glass - themed_loading_screen restyled; players and boot splash follow Performance - drop every BackdropFilter: backdrop blur forced a full framebuffer read per frame per card, the main source of scroll jank - bundle Fraunces/Karla as assets so google_fonts stops fetching from fonts.gstatic.com at startup - bound image decode size on 12 sites: a 40-poster grid drops from ~240 MB to a few tens of MB of GPU memory Players (new shared engine: web/xf-player-core.js) - start on media events instead of polling the buffer every 100-200 ms - hls.js/mpegts.js loaded on demand, so ~213 KB of mpegts is never fetched for HLS streams - liveSyncDurationCount 10 -> 2, startFragPrefetch on, testBandwidth off, MPEG-TS initial stash 512 KB -> 64 KB - adaptive escalation fast -> balanced -> safe after repeated stalls in a 60 s window, applied live for HLS so there is no visible cut Preserved from the recent streaming work on main: server-side quality selection, stable player viewId, the turbo direct MPEG-TS path, vendored library cache headers and the CI/Docker changes. Only the theme layer and the player front-end were replaced; GlassCard stays in ui_components. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
632 lines
21 KiB
Dart
632 lines
21 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'app_colors.dart';
|
|
|
|
/// XtremFlow — Thème « Warm Cinema »
|
|
///
|
|
/// Typo : Fraunces (display, serif éditorial) + Karla (corps / UI).
|
|
/// Style : charbon chaud, crème, orange brûlé. Profondeur par l'ombre,
|
|
/// jamais par le flou — aucun `BackdropFilter` dans tout le thème.
|
|
class AppTheme {
|
|
AppTheme._();
|
|
|
|
// ============ ESPACEMENT (rythme 4 / 8) ============
|
|
static const double spacingXs = 4.0;
|
|
static const double spacingSm = 8.0;
|
|
static const double spacingBase = 8.0;
|
|
static const double spacingMd = 16.0;
|
|
static const double spacingLg = 24.0;
|
|
static const double spacingXl = 32.0;
|
|
static const double spacing2xl = 48.0;
|
|
static const double spacing3xl = 80.0;
|
|
|
|
// Alias numériques (utilisés dans plusieurs écrans)
|
|
static const double spacing4 = 4.0;
|
|
static const double spacing8 = 8.0;
|
|
static const double spacing12 = 12.0;
|
|
static const double spacing16 = 16.0;
|
|
static const double spacing24 = 24.0;
|
|
static const double spacing32 = 32.0;
|
|
static const double spacing48 = 48.0;
|
|
|
|
// ============ RAYONS ============
|
|
// Cinéma : angles doux mais assumés, jamais des bulles.
|
|
static const double radiusNone = 0.0;
|
|
static const double radiusSm = 6.0;
|
|
static const double radiusMd = 10.0;
|
|
static const double radiusLg = 14.0;
|
|
static const double radiusXl = 20.0;
|
|
static const double radiusFull = 9999.0;
|
|
|
|
// ============ ANIMATION ============
|
|
// Rythme lent, « fondu de projection ».
|
|
static const Duration durationXs = Duration(milliseconds: 120);
|
|
static const Duration durationFast = Duration(milliseconds: 180);
|
|
static const Duration durationNormal = Duration(milliseconds: 260);
|
|
static const Duration durationSm = Duration(milliseconds: 180);
|
|
static const Duration durationMd = Duration(milliseconds: 260);
|
|
static const Duration durationLg = Duration(milliseconds: 380);
|
|
static const Duration durationXl = Duration(milliseconds: 520);
|
|
|
|
static const Curve curveDefault = Curves.easeOutCubic;
|
|
static const Curve curveSmooth = Curves.easeOutCubic;
|
|
|
|
/// Courbe « entrée cinéma » : démarre vite, s'installe lentement.
|
|
static const Curve curveCinema = Cubic(0.16, 1.0, 0.3, 1.0);
|
|
|
|
// ============ TYPOGRAPHIE ============
|
|
/// Display serif éditorial — titres, héros, chiffres marquants.
|
|
static TextStyle display({
|
|
required double fontSize,
|
|
required FontWeight fontWeight,
|
|
double? letterSpacing,
|
|
double? height,
|
|
Color color = AppColors.onSurface,
|
|
FontStyle? fontStyle,
|
|
}) {
|
|
return GoogleFonts.fraunces(
|
|
fontSize: fontSize,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: letterSpacing,
|
|
height: height,
|
|
color: color,
|
|
fontStyle: fontStyle,
|
|
);
|
|
}
|
|
|
|
/// Corps / UI — humaniste chaud, très lisible à distance TV.
|
|
static TextStyle body({
|
|
required double fontSize,
|
|
required FontWeight fontWeight,
|
|
double? letterSpacing,
|
|
double? height,
|
|
Color color = AppColors.onSurface,
|
|
}) {
|
|
return GoogleFonts.karla(
|
|
fontSize: fontSize,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: letterSpacing,
|
|
height: height,
|
|
color: color,
|
|
);
|
|
}
|
|
|
|
/// Micro-label capitales espacées — « GÉNÉRIQUE DE FILM ».
|
|
static TextStyle eyebrow({
|
|
double fontSize = 11,
|
|
Color color = AppColors.textTertiary,
|
|
FontWeight fontWeight = FontWeight.w700,
|
|
}) {
|
|
return GoogleFonts.karla(
|
|
fontSize: fontSize,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: 1.6,
|
|
height: 1.1,
|
|
color: color,
|
|
);
|
|
}
|
|
|
|
// Alias internes historiques
|
|
static TextStyle _display({
|
|
required double fontSize,
|
|
required FontWeight fontWeight,
|
|
double? letterSpacing,
|
|
double? height,
|
|
Color color = AppColors.onSurface,
|
|
}) =>
|
|
display(
|
|
fontSize: fontSize,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: letterSpacing,
|
|
height: height,
|
|
color: color,
|
|
);
|
|
|
|
static TextStyle _body({
|
|
required double fontSize,
|
|
required FontWeight fontWeight,
|
|
double? letterSpacing,
|
|
double? height,
|
|
Color color = AppColors.onSurface,
|
|
}) =>
|
|
body(
|
|
fontSize: fontSize,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: letterSpacing,
|
|
height: height,
|
|
color: color,
|
|
);
|
|
|
|
// ============ THÈME SOMBRE (principal) ============
|
|
static ThemeData get darkTheme {
|
|
final baseTheme = ThemeData.dark(useMaterial3: true);
|
|
|
|
return baseTheme.copyWith(
|
|
colorScheme: AppColors.darkColorScheme,
|
|
scaffoldBackgroundColor: AppColors.background,
|
|
canvasColor: AppColors.background,
|
|
splashColor: AppColors.primaryContainer.withValues(alpha: 0.12),
|
|
highlightColor: AppColors.primaryContainer.withValues(alpha: 0.08),
|
|
|
|
textTheme: TextTheme(
|
|
// Display — Fraunces, serrée, autoritaire
|
|
displayLarge: _display(
|
|
fontSize: 56,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: -1.4,
|
|
height: 1.02,
|
|
),
|
|
displayMedium: _display(
|
|
fontSize: 40,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.9,
|
|
height: 1.08,
|
|
),
|
|
displaySmall: _display(
|
|
fontSize: 30,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.5,
|
|
height: 1.15,
|
|
),
|
|
headlineLarge: _display(
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.6,
|
|
height: 1.12,
|
|
),
|
|
headlineMedium: _display(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.3,
|
|
height: 1.2,
|
|
),
|
|
headlineSmall: _display(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.2,
|
|
height: 1.25,
|
|
),
|
|
titleLarge: _display(
|
|
fontSize: 19,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.1,
|
|
),
|
|
titleMedium: _body(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.1,
|
|
),
|
|
titleSmall: _body(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.2,
|
|
),
|
|
// Corps — Karla
|
|
bodyLarge: _body(
|
|
fontSize: 17,
|
|
fontWeight: FontWeight.w400,
|
|
height: 1.62,
|
|
color: AppColors.onSurfaceVariant,
|
|
),
|
|
bodyMedium: _body(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w400,
|
|
height: 1.55,
|
|
color: AppColors.onSurfaceVariant,
|
|
),
|
|
bodySmall: _body(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w400,
|
|
height: 1.45,
|
|
color: AppColors.onSurfaceVariant,
|
|
),
|
|
// Labels — capitales espacées, esprit générique de film
|
|
labelLarge: _body(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 1.1,
|
|
height: 1.2,
|
|
),
|
|
labelMedium: _body(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.9,
|
|
height: 1.2,
|
|
),
|
|
labelSmall: _body(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 1.4,
|
|
height: 1.2,
|
|
color: AppColors.textTertiary,
|
|
),
|
|
),
|
|
|
|
// AppBar : plate, sans élévation, le fond fait le travail
|
|
appBarTheme: AppBarTheme(
|
|
backgroundColor: Colors.transparent,
|
|
surfaceTintColor: Colors.transparent,
|
|
foregroundColor: AppColors.onSurface,
|
|
elevation: 0,
|
|
centerTitle: false,
|
|
scrolledUnderElevation: 0,
|
|
titleTextStyle: _display(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.3,
|
|
),
|
|
),
|
|
|
|
// Champs : creusés dans la surface, liseré ember au focus
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
filled: true,
|
|
fillColor: AppColors.surfaceContainerLow,
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: spacingMd,
|
|
vertical: spacingMd,
|
|
),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
borderSide: const BorderSide(color: AppColors.outlineVariant, width: 1),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
borderSide:
|
|
const BorderSide(color: AppColors.primaryContainer, width: 2),
|
|
),
|
|
errorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
borderSide: const BorderSide(color: AppColors.error, width: 1),
|
|
),
|
|
focusedErrorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
borderSide: const BorderSide(color: AppColors.error, width: 2),
|
|
),
|
|
hintStyle: _body(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w400,
|
|
color: AppColors.textTertiary,
|
|
),
|
|
labelStyle: eyebrow(color: AppColors.onSurfaceVariant),
|
|
floatingLabelStyle: eyebrow(color: AppColors.primary),
|
|
),
|
|
|
|
// Boutons pleins : ember franc, coins nets
|
|
filledButtonTheme: FilledButtonThemeData(
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: AppColors.primaryFill,
|
|
foregroundColor: AppColors.onSurface,
|
|
disabledBackgroundColor: AppColors.surfaceContainerHigh,
|
|
disabledForegroundColor: AppColors.textTertiary,
|
|
elevation: 0,
|
|
padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 16),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
),
|
|
textStyle: _body(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 1.1,
|
|
),
|
|
).copyWith(
|
|
overlayColor: WidgetStateProperty.resolveWith((states) {
|
|
if (states.contains(WidgetState.pressed)) {
|
|
return Colors.black.withValues(alpha: 0.18);
|
|
}
|
|
if (states.contains(WidgetState.hovered) ||
|
|
states.contains(WidgetState.focused)) {
|
|
return AppColors.onSurface.withValues(alpha: 0.12);
|
|
}
|
|
return null;
|
|
}),
|
|
),
|
|
),
|
|
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.surfaceContainerHigh,
|
|
foregroundColor: AppColors.onSurface,
|
|
elevation: 0,
|
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
),
|
|
textStyle: _body(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 1.1,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Boutons contour : liseré crème discret
|
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.onSurface,
|
|
side: const BorderSide(color: AppColors.outlineVariant, width: 1),
|
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
),
|
|
textStyle: _body(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 1.1,
|
|
),
|
|
),
|
|
),
|
|
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: AppColors.primary,
|
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
|
textStyle: _body(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.8,
|
|
),
|
|
),
|
|
),
|
|
|
|
iconButtonTheme: IconButtonThemeData(
|
|
style: IconButton.styleFrom(
|
|
foregroundColor: AppColors.onSurfaceVariant,
|
|
hoverColor: AppColors.primaryContainer.withValues(alpha: 0.14),
|
|
highlightColor: AppColors.primaryContainer.withValues(alpha: 0.2),
|
|
),
|
|
),
|
|
|
|
// Cartes : surface opaque + ombre profonde (zéro blur)
|
|
cardTheme: CardThemeData(
|
|
color: AppColors.surfaceContainerLow,
|
|
surfaceTintColor: Colors.transparent,
|
|
shadowColor: Colors.black,
|
|
elevation: 0,
|
|
margin: EdgeInsets.zero,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusLg),
|
|
side: const BorderSide(color: AppColors.glassLevel1Border, width: 1),
|
|
),
|
|
),
|
|
|
|
// Dialogues : bloc opaque flottant
|
|
dialogTheme: DialogThemeData(
|
|
backgroundColor: AppColors.surfaceContainer,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusXl),
|
|
side: const BorderSide(color: AppColors.glassLevel2Border, width: 1),
|
|
),
|
|
titleTextStyle: _display(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.3,
|
|
),
|
|
contentTextStyle: _body(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w400,
|
|
height: 1.55,
|
|
color: AppColors.onSurfaceVariant,
|
|
),
|
|
),
|
|
|
|
bottomSheetTheme: const BottomSheetThemeData(
|
|
backgroundColor: AppColors.surfaceContainer,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(radiusXl)),
|
|
),
|
|
),
|
|
|
|
snackBarTheme: SnackBarThemeData(
|
|
backgroundColor: AppColors.surfaceContainerHighest,
|
|
contentTextStyle: _body(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.onSurface,
|
|
),
|
|
actionTextColor: AppColors.primary,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
),
|
|
behavior: SnackBarBehavior.floating,
|
|
elevation: 0,
|
|
),
|
|
|
|
// Chips : étiquettes de bobine
|
|
chipTheme: ChipThemeData(
|
|
backgroundColor: AppColors.surfaceContainerHigh,
|
|
deleteIconColor: AppColors.onSurfaceVariant,
|
|
disabledColor: AppColors.surfaceContainer,
|
|
selectedColor: AppColors.primaryFill,
|
|
secondarySelectedColor: AppColors.primaryFill,
|
|
checkmarkColor: AppColors.onSurface,
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
side: const BorderSide(color: AppColors.outlineVariant, width: 1),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusSm),
|
|
),
|
|
labelStyle: _body(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.6,
|
|
color: AppColors.onSurfaceVariant,
|
|
),
|
|
secondaryLabelStyle: _body(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.6,
|
|
color: AppColors.onSurface,
|
|
),
|
|
),
|
|
|
|
// Onglets : soulignement ember épais
|
|
tabBarTheme: TabBarThemeData(
|
|
labelColor: AppColors.onSurface,
|
|
unselectedLabelColor: AppColors.textTertiary,
|
|
indicatorColor: AppColors.primaryContainer,
|
|
indicatorSize: TabBarIndicatorSize.label,
|
|
dividerColor: Colors.transparent,
|
|
labelStyle: _body(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: 0.8,
|
|
),
|
|
unselectedLabelStyle: _body(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: 0.8,
|
|
),
|
|
),
|
|
|
|
navigationRailTheme: NavigationRailThemeData(
|
|
backgroundColor: AppColors.surfaceContainerLowest,
|
|
indicatorColor: AppColors.primaryContainer.withValues(alpha: 0.22),
|
|
selectedIconTheme: const IconThemeData(color: AppColors.primary),
|
|
unselectedIconTheme: const IconThemeData(color: AppColors.textTertiary),
|
|
selectedLabelTextStyle: eyebrow(color: AppColors.primary),
|
|
unselectedLabelTextStyle: eyebrow(color: AppColors.textTertiary),
|
|
),
|
|
|
|
navigationBarTheme: NavigationBarThemeData(
|
|
backgroundColor: AppColors.surfaceContainerLowest,
|
|
surfaceTintColor: Colors.transparent,
|
|
indicatorColor: AppColors.primaryContainer.withValues(alpha: 0.22),
|
|
elevation: 0,
|
|
labelTextStyle: WidgetStateProperty.resolveWith(
|
|
(states) => eyebrow(
|
|
color: states.contains(WidgetState.selected)
|
|
? AppColors.primary
|
|
: AppColors.textTertiary,
|
|
),
|
|
),
|
|
iconTheme: WidgetStateProperty.resolveWith(
|
|
(states) => IconThemeData(
|
|
color: states.contains(WidgetState.selected)
|
|
? AppColors.primary
|
|
: AppColors.textTertiary,
|
|
size: 24,
|
|
),
|
|
),
|
|
),
|
|
|
|
dividerTheme: const DividerThemeData(
|
|
color: AppColors.outlineVariant,
|
|
thickness: 1,
|
|
space: 1,
|
|
),
|
|
|
|
scrollbarTheme: ScrollbarThemeData(
|
|
thumbColor: WidgetStateProperty.all(AppColors.outlineVariant),
|
|
trackColor: WidgetStateProperty.all(Colors.transparent),
|
|
radius: const Radius.circular(2),
|
|
thickness: WidgetStateProperty.all(5),
|
|
),
|
|
|
|
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
|
color: AppColors.primaryContainer,
|
|
linearTrackColor: AppColors.surfaceContainerHigh,
|
|
circularTrackColor: Colors.transparent,
|
|
),
|
|
|
|
sliderTheme: SliderThemeData(
|
|
activeTrackColor: AppColors.primaryContainer,
|
|
inactiveTrackColor: AppColors.surfaceContainerHigh,
|
|
thumbColor: AppColors.primary,
|
|
overlayColor: AppColors.primaryContainer.withValues(alpha: 0.18),
|
|
trackHeight: 4,
|
|
),
|
|
|
|
switchTheme: SwitchThemeData(
|
|
thumbColor: WidgetStateProperty.resolveWith((states) {
|
|
if (states.contains(WidgetState.selected)) return AppColors.onSurface;
|
|
return AppColors.outline;
|
|
}),
|
|
trackColor: WidgetStateProperty.resolveWith((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return AppColors.primaryContainer;
|
|
}
|
|
return AppColors.surfaceContainerHigh;
|
|
}),
|
|
trackOutlineColor:
|
|
WidgetStateProperty.all(AppColors.outlineVariant),
|
|
),
|
|
|
|
checkboxTheme: CheckboxThemeData(
|
|
fillColor: WidgetStateProperty.resolveWith((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return AppColors.primaryContainer;
|
|
}
|
|
return Colors.transparent;
|
|
}),
|
|
checkColor: WidgetStateProperty.all(AppColors.onSurface),
|
|
side: const BorderSide(color: AppColors.outline, width: 1.5),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
|
|
tooltipTheme: TooltipThemeData(
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceContainerHighest,
|
|
borderRadius: BorderRadius.circular(radiusSm),
|
|
border: Border.all(color: AppColors.outlineVariant),
|
|
),
|
|
textStyle: _body(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.onSurface,
|
|
),
|
|
),
|
|
|
|
listTileTheme: ListTileThemeData(
|
|
iconColor: AppColors.onSurfaceVariant,
|
|
textColor: AppColors.onSurface,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
),
|
|
),
|
|
|
|
popupMenuTheme: PopupMenuThemeData(
|
|
color: AppColors.surfaceContainerHigh,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(radiusMd),
|
|
side: const BorderSide(color: AppColors.outlineVariant),
|
|
),
|
|
textStyle: _body(fontSize: 14, fontWeight: FontWeight.w500),
|
|
),
|
|
);
|
|
}
|
|
|
|
// ============ THÈME CLAIR (« papier kraft ») ============
|
|
static ThemeData get lightTheme {
|
|
final scheme = AppColors.lightColorScheme;
|
|
return ThemeData.light(useMaterial3: true).copyWith(
|
|
colorScheme: scheme,
|
|
scaffoldBackgroundColor: scheme.surface,
|
|
textTheme: GoogleFonts.karlaTextTheme(ThemeData.light().textTheme),
|
|
appBarTheme: AppBarTheme(
|
|
backgroundColor: Colors.transparent,
|
|
surfaceTintColor: Colors.transparent,
|
|
foregroundColor: scheme.onSurface,
|
|
elevation: 0,
|
|
centerTitle: false,
|
|
titleTextStyle: GoogleFonts.fraunces(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.3,
|
|
color: scheme.onSurface,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|