import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/game/game_state.dart'; import '../../l10n/app_localizations.dart'; import '../providers/gen_filter_provider.dart'; import '../providers/game_provider.dart'; import '../providers/navigation_provider.dart'; import '../widgets/scanline_overlay.dart'; import '../widgets/guess/guess_silhouette.dart'; import '../widgets/guess/gen_filter_section.dart'; import '../widgets/guess/lives_row.dart'; import '../widgets/guess/guess_input_section.dart'; import '../widgets/guess/score_board.dart'; /// Page de jeu "devine le Pokémon" : orchestre gameProvider et compose les sous-widgets. class GuessPage extends ConsumerStatefulWidget { const GuessPage({Key? key}) : super(key: key); @override ConsumerState createState() => _GuessPageState(); } class _GuessPageState extends ConsumerState { final TextEditingController _guessController = TextEditingController(); bool _started = false; bool _genFilterOpen = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { if (!_started) { _started = true; ref.read(gameProvider.notifier).startNewGame(); } }); } @override void dispose() { _guessController.dispose(); super.dispose(); } Future _onGuess() async { final result = await ref.read(gameProvider.notifier).submitGuess(_guessController.text); if (!mounted) return; final l = AppLocalizations.of(context)!; final state = ref.read(gameProvider); switch (result) { case GuessResult.correct: final name = state.currentPokemon!.formatedName; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(state.isShiny ? l.caughtShiny(name) : l.caughtNormal(name)), backgroundColor: state.isShiny ? Colors.amber[800] : Colors.green, )); break; case GuessResult.wrong: ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(l.wrongGuess), backgroundColor: Colors.orange)); break; case GuessResult.gameOver: await _showGameOver(); break; case GuessResult.invalid: break; } _guessController.clear(); } Future _showGameOver() async { final state = ref.read(gameProvider); final playAgain = await Navigator.pushNamed( context, '/game-over', arguments: { 'pokemonName': state.currentPokemon!.formatedName, 'score': state.currentScore, 'streak': state.sessionCorrectCount, 'pokemonImage': state.currentPokemon!.imageUrl, }, ) as bool?; if (!mounted) return; if (playAgain == false) { ref.read(selectedTabProvider.notifier).set(0); // onglet LIST } // true / false / null (geste retour) relancent tous une partie pour ne pas rester bloqué. await ref.read(gameProvider.notifier).startNewGame(); } @override Widget build(BuildContext context) { final state = ref.watch(gameProvider); if (state.status == GameStatus.loading) { return const Center(child: CircularProgressIndicator()); } if (state.currentPokemon == null || state.status == GameStatus.error) { return Center(child: Text(AppLocalizations.of(context)!.errorLoadingPokemon)); } final pokemon = state.currentPokemon!; final isGuessed = state.status == GameStatus.roundWon; final notifier = ref.read(gameProvider.notifier); return Container( decoration: const BoxDecoration(color: Color(0xFFC8D1D8)), child: Stack( children: [ const ScanlineOverlay(), SingleChildScrollView( child: Column( children: [ GuessSilhouette(pokemon: pokemon, isShiny: state.isShiny, isGuessed: isGuessed), GenFilterSection( isOpen: _genFilterOpen, onToggleOpen: () => setState(() => _genFilterOpen = !_genFilterOpen), selectedGens: ref.watch(genFilterProvider), onToggle: (i) => ref.read(genFilterProvider.notifier).toggle(i), ), const SizedBox(height: 12), LivesRow(lives: state.lives), const SizedBox(height: 16), GuessInputSection( controller: _guessController, pokemonName: pokemon.formatedName, isHintUsed: state.isHintUsed, isGuessed: isGuessed, hints: state.hints, skips: state.skips, onGuess: _onGuess, onContinue: () => notifier.loadNextPokemon(), onHint: (state.isHintUsed || state.hints <= 0) ? null : () => notifier.useHint(), onSkip: state.skips > 0 ? () => notifier.useSkip() : null, ), const SizedBox(height: 24), Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: ScoreBoard(currentScore: state.currentScore, bestScore: state.bestScore), ), const SizedBox(height: 32), ], ), ), ], ), ); } }