feat(ui): identité visuelle Court Blazer sur toute l'app (ticket #17)

Ajoute le thème centralisé (presentation/theme.dart) basé sur les
polices Anton/Archivo et l'applique à tous les écrans (accueil,
bibliothèque d'exercices, programme, séance-modèle, exécution de
séance, historique). flutter analyze propre, 27/27 tests verts, build
APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 23:46:26 +02:00
parent 202e9d8db3
commit 2686c175f3
10 changed files with 535 additions and 81 deletions

View File

@ -5,13 +5,21 @@ import '../domain/domain.dart';
import 'exercise_library_screen.dart';
import 'history_screen.dart';
import 'program_screen.dart';
import 'theme.dart';
import 'workout_execution_screen.dart';
import 'workout_template_screen.dart';
final class HomeScreen extends StatefulWidget {
const HomeScreen({required this.bootstrap, super.key});
const HomeScreen({
required this.bootstrap,
this.themeMode = ThemeMode.system,
this.onThemeModeChanged,
super.key,
});
final AppBootstrap bootstrap;
final ThemeMode themeMode;
final ValueChanged<ThemeMode>? onThemeModeChanged;
@override
State<HomeScreen> createState() => _HomeScreenState();
@ -29,7 +37,22 @@ final class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('GameTime')),
appBar: AppBar(
title: const GameTimeLogo(),
actions: [
PopupMenuButton<ThemeMode>(
tooltip: 'Thème',
initialValue: widget.themeMode,
icon: const Icon(Icons.contrast),
onSelected: widget.onThemeModeChanged,
itemBuilder: (context) => const [
PopupMenuItem(value: ThemeMode.system, child: Text('Système')),
PopupMenuItem(value: ThemeMode.light, child: Text('Clair')),
PopupMenuItem(value: ThemeMode.dark, child: Text('Sombre')),
],
),
],
),
body: ListView(
children: [
FutureBuilder<ActiveWorkoutSession?>(
@ -46,17 +69,20 @@ final class _HomeScreenState extends State<HomeScreen> {
setIndex: session.currentSetIndex,
);
final exercise = plan.exerciseAt(position);
return MaterialBanner(
content: Text(
'Séance en cours — Reprendre · ${exercise.name}, '
'série ${position.setIndex + 1}/${exercise.setsCount}',
),
actions: [
TextButton(
onPressed: () => _resume(session),
child: const Text('Reprendre'),
return CourtBlazerAccentPanel(
margin: const EdgeInsets.all(12),
child: MaterialBanner(
content: Text(
'Séance en cours — Reprendre · ${exercise.name}, '
'série ${position.setIndex + 1}/${exercise.setsCount}',
),
],
actions: [
TextButton(
onPressed: () => _resume(session),
child: const Text('Reprendre'),
),
],
),
);
},
),