import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flame/game.dart'; import 'package:match_three/game/models/gem.dart'; import '../game/match_three_game.dart'; import '../bloc/game_bloc.dart'; class GameScreen extends StatefulWidget { final int levelId; final int gridHeight; final int gridWidth; const GameScreen( {super.key, this.levelId = 0, this.gridHeight = 5, this.gridWidth = 5}); @override State createState() => _GameScreenState(); } class _GameScreenState extends State { late MatchThreeGame game; final bool isDebug = true; @override void initState() { super.initState(); game = MatchThreeGame( levelId: widget.levelId, gridHeight: widget.gridHeight, gridWidth: widget.gridWidth); } @override Widget build(BuildContext context) { return Scaffold( body: BlocListener( listener: (context, state) { if (game.gridComponent == null) return; if (state is GameLevelPlaying) { game.gridComponent!.updateGrid(state); } else if (state is GameLevelCompleted) { _showLevelCompletedDialog(context, state); } else if (state is GameLevelFailed) { _showLevelFailedDialog(context, state); } }, child: BlocBuilder( builder: (context, state) { // Set game bloc reference game.setGameBloc(context.read()); return Stack( children: [ GameWidget.controlled( gameFactory: () => game, ), if (state is GameLevelPlaying) Positioned( top: 50, left: 20, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( state.level.name, style: const TextStyle( color: Colors.amber, fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), if (isDebug) Text( 'State: ${state.runtimeType}', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( 'Score: ${state.score}', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), if (state.level.constraints.hasScoreTarget) Text( 'Target: ${state.level.constraints.targetScore}', style: const TextStyle( color: Colors.green, fontSize: 14, ), ), Text( 'Moves: ${state.moves}', style: const TextStyle( color: Colors.white, fontSize: 16, ), ), if (state.level.constraints.hasMoveLimit) Text( 'Limit: ${state.level.constraints.maxMoves}', style: TextStyle( color: state.moves >= state.level.constraints.maxMoves! ? Colors.red : Colors.orange, fontSize: 14, ), ), if (state.level.constraints.hasTimeLimit) Text( 'Time: ${state.level.constraints.timeLimit! - state.timeElapsed}s', style: TextStyle( color: (state.level.constraints.timeLimit! - state.timeElapsed) <= 10 ? Colors.red : Colors.cyan, fontSize: 16, fontWeight: FontWeight.bold, ), ), if (state.level.objectives.hasGemTypeObjectives) ...[ const SizedBox(height: 4), const Text( 'Objectives:', style: TextStyle( color: Colors.yellow, fontSize: 14, fontWeight: FontWeight.bold, ), ), ...state.level.objectives.clearGemTypes.entries .map((entry) { final gemType = Gem.getName(entry.key); final required = entry.value; final cleared = state.gemsCleared[entry.key] ?? 0; return Text( 'Gem $gemType: $cleared/$required', style: TextStyle( color: cleared >= required ? Colors.green : Colors.white, fontSize: 12, ), ); }), ], if (state is GameLevelMatch) Text( 'Combo x ${state.combo + 1}', style: const TextStyle( color: Colors.white, fontSize: 16, ), ), ], ), ), ), Positioned( top: 50, right: 20, child: IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, color: Colors.white, size: 30), ), ), ], ); }, ), ), ); } void _showLevelCompletedDialog( BuildContext context, GameLevelCompleted state) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: const Text( 'Level Complete!', style: TextStyle( color: Colors.green, fontWeight: FontWeight.bold, ), ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( state.level.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(3, (index) { return Icon( index < state.stars ? Icons.star : Icons.star_border, color: index < state.stars ? Colors.amber : Colors.grey, size: 32, ); }), ), const SizedBox(height: 16), Text( 'Final Score: ${state.score}', style: const TextStyle(fontSize: 16), ), Text( 'Moves Used: ${state.moves}', style: const TextStyle(fontSize: 16), ), if (state.level.constraints.hasTimeLimit) Text( 'Time: ${state.timeElapsed}s', style: const TextStyle(fontSize: 16), ), if (state.level.objectives.hasGemTypeObjectives) ...[ const SizedBox(height: 8), const Text( 'Objectives Completed:', style: TextStyle(fontWeight: FontWeight.bold), ), ...state.level.objectives.clearGemTypes.entries.map((entry) { final gemType = entry.key; final required = entry.value; final cleared = state.gemsCleared[gemType] ?? 0; return Text( 'Gem $gemType: $cleared/$required ✓', style: const TextStyle(color: Colors.green), ); }), ], ], ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); // Close dialog Navigator.of(context).pop(); // Return to level selection }, child: const Text('Continue'), ), TextButton( onPressed: () { Navigator.of(context).pop(); // Close dialog context.read().add(StartLevel(state.level.id)); }, child: const Text('Replay'), ), ], ); }, ); } void _showLevelFailedDialog(BuildContext context, GameLevelFailed state) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: const Text( 'Level Failed', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( state.level.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Text( state.reason, style: const TextStyle( fontSize: 16, color: Colors.red, ), textAlign: TextAlign.center, ), const SizedBox(height: 16), Text( 'Final Score: ${state.score}', style: const TextStyle(fontSize: 16), ), Text( 'Moves Used: ${state.moves}', style: const TextStyle(fontSize: 16), ), if (state.level.constraints.hasTimeLimit) Text( 'Time: ${state.timeElapsed}s', style: const TextStyle(fontSize: 16), ), if (state.level.objectives.hasGemTypeObjectives) ...[ const SizedBox(height: 8), const Text( 'Objectives Progress:', style: TextStyle(fontWeight: FontWeight.bold), ), ...state.level.objectives.clearGemTypes.entries.map((entry) { final gemType = entry.key; final required = entry.value; final cleared = state.gemsCleared[gemType] ?? 0; final completed = cleared >= required; return Text( 'Gem $gemType: $cleared/$required ${completed ? "✓" : "✗"}', style: TextStyle( color: completed ? Colors.green : Colors.red, ), ); }), ], ], ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); // Close dialog Navigator.of(context).pop(); // Return to level selection }, child: const Text('Give Up'), ), TextButton( onPressed: () { Navigator.of(context).pop(); // Close dialog context.read().add(StartLevel(state.level.id)); }, child: const Text('Try Again'), ), ], ); }, ); } }