2026-03-11 22:00:01 +01:00
// ===========================================================================
// FILE: lib/widgets/game_over_dialog.dart
// ===========================================================================
2026-02-27 23:35:54 +01:00
import ' package:flutter/material.dart ' ;
import ' package:provider/provider.dart ' ;
import ' ../logic/game_controller.dart ' ;
import ' ../core/theme_manager.dart ' ;
import ' ../core/app_colors.dart ' ;
2026-03-11 22:00:01 +01:00
import ' ../services/storage_service.dart ' ;
2026-03-23 01:00:01 +01:00
import ' painters.dart ' ;
2026-02-27 23:35:54 +01:00
class GameOverDialog extends StatelessWidget {
const GameOverDialog ( { super . key } ) ;
@ override
Widget build ( BuildContext context ) {
final game = context . read < GameController > ( ) ;
final themeManager = context . read < ThemeManager > ( ) ;
final theme = themeManager . currentColors ;
final themeType = themeManager . currentThemeType ;
2026-03-23 01:00:01 +01:00
Color inkColor = const Color ( 0xFF111122 ) ;
2026-02-27 23:35:54 +01:00
int red = game . board . scoreRed ;
int blue = game . board . scoreBlue ;
bool playerBeatCPU = game . isVsCPU & & red > blue ;
2026-03-11 22:00:01 +01:00
String myName = StorageService . instance . playerName . toUpperCase ( ) ;
if ( myName . isEmpty ) myName = " TU " ;
2026-02-27 23:35:54 +01:00
// --- LOGICA NOMI ---
2026-03-11 22:00:01 +01:00
String nameRed = myName ;
String nameBlue = themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . arcade ? " VERDE " : " BLU " ;
2026-02-27 23:35:54 +01:00
if ( game . isOnline ) {
nameRed = game . onlineHostName . toUpperCase ( ) ;
nameBlue = game . onlineGuestName . toUpperCase ( ) ;
} else if ( game . isVsCPU ) {
2026-03-11 22:00:01 +01:00
nameRed = myName ;
2026-02-27 23:35:54 +01:00
nameBlue = " CPU " ;
}
// --- DETERMINA IL VINCITORE ---
String winnerText = " " ;
Color winnerColor = theme . text ;
if ( red > blue ) {
winnerText = " VINCE $ nameRed ! " ;
winnerColor = theme . playerRed ;
} else if ( blue > red ) {
winnerText = " VINCE $ nameBlue ! " ;
winnerColor = theme . playerBlue ;
} else {
winnerText = " PAREGGIO! " ;
2026-03-23 01:00:01 +01:00
winnerColor = themeType = = AppThemeType . doodle ? inkColor : theme . text ;
2026-02-27 23:35:54 +01:00
}
2026-03-23 01:00:01 +01:00
Widget dialogContent = Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( winnerText , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 26 , fontWeight: FontWeight . w900 , color: winnerColor ) ) ) ,
const SizedBox ( height: 20 ) ,
Container (
padding: const EdgeInsets . symmetric ( horizontal: 20 , vertical: 10 ) ,
decoration: BoxDecoration (
color: themeType = = AppThemeType . doodle ? Colors . transparent : theme . text . withOpacity ( 0.05 ) ,
borderRadius: BorderRadius . circular ( 15 ) ,
border: themeType = = AppThemeType . doodle ? Border . all ( color: inkColor . withOpacity ( 0.3 ) , width: 1.5 ) : null ,
) ,
child: FittedBox (
fit: BoxFit . scaleDown ,
child: Row (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " $ nameRed : $ red " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 16 , fontWeight: FontWeight . bold , color: theme . playerRed ) ) ) ,
Text ( " - " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 18 , color: themeType = = AppThemeType . doodle ? inkColor : theme . text ) ) ) ,
Text ( " $ nameBlue : $ blue " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 16 , fontWeight: FontWeight . bold , color: theme . playerBlue ) ) ) ,
] ,
) ,
) ,
) ,
if ( game . lastMatchXP > 0 ) . . . [
const SizedBox ( height: 15 ) ,
Container (
padding: const EdgeInsets . symmetric ( horizontal: 14 , vertical: 6 ) ,
decoration: BoxDecoration (
color: Colors . green . withOpacity ( 0.15 ) ,
borderRadius: BorderRadius . circular ( 20 ) ,
border: Border . all ( color: themeType = = AppThemeType . doodle ? Colors . green . shade700 : Colors . greenAccent , width: 1.5 ) ,
boxShadow: ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) ? [ const BoxShadow ( color: Colors . greenAccent , blurRadius: 10 , spreadRadius: - 5 ) ] : [ ] ,
) ,
child: Text ( " + ${ game . lastMatchXP } XP " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? Colors . green . shade700 : Colors . greenAccent , fontWeight: FontWeight . w900 , fontSize: 16 , letterSpacing: 1.5 ) ) ) ,
) ,
] ,
if ( game . isVsCPU ) . . . [
const SizedBox ( height: 15 ) ,
Text ( " Difficoltà CPU: Livello ${ game . cpuLevel } " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w500 , color: themeType = = AppThemeType . doodle ? inkColor . withOpacity ( 0.7 ) : theme . text . withOpacity ( 0.7 ) ) ) ) ,
] ,
if ( game . isOnline ) . . . [
const SizedBox ( height: 20 ) ,
if ( game . rematchRequested & & ! game . opponentWantsRematch )
Text ( " In attesa di $ nameBlue ... " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? Colors . orange . shade700 : Colors . amber , fontWeight: FontWeight . bold , fontStyle: FontStyle . italic ) ) ) ,
if ( game . opponentWantsRematch & & ! game . rematchRequested )
Text ( " $ nameBlue vuole la rivincita! " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? Colors . green . shade700 : Colors . greenAccent , fontWeight: FontWeight . bold ) ) ) ,
if ( game . rematchRequested & & game . opponentWantsRematch )
Text ( " Avvio nuova partita... " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? Colors . green . shade800 : Colors . green , fontWeight: FontWeight . bold ) ) ) ,
] ,
// --- SEZIONE LEVEL UP E ROADMAP DINAMICA ---
if ( game . hasLeveledUp & & game . unlockedRewards . isNotEmpty ) . . . [
const SizedBox ( height: 30 ) ,
Divider ( color: themeType = = AppThemeType . doodle ? inkColor . withOpacity ( 0.3 ) : theme . text . withOpacity ( 0.2 ) ) ,
const SizedBox ( height: 15 ) ,
Container (
padding: const EdgeInsets . symmetric ( vertical: 8 , horizontal: 20 ) ,
decoration: BoxDecoration (
color: themeType = = AppThemeType . doodle ? Colors . amber . withOpacity ( 0.1 ) : Colors . amber . withOpacity ( 0.2 ) ,
borderRadius: BorderRadius . circular ( 30 ) ,
border: Border . all ( color: themeType = = AppThemeType . doodle ? Colors . amber . shade700 : Colors . amber , width: 2 )
) ,
child: Text ( " 🎉 LIVELLO ${ game . newlyReachedLevel } ! 🎉 " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? Colors . amber . shade700 : Colors . amber , fontWeight: FontWeight . w900 , fontSize: 18 ) ) ) ,
) ,
const SizedBox ( height: 15 ) ,
. . . game . unlockedRewards . map ( ( reward ) {
Color rewardColor = themeType = = AppThemeType . doodle ? ( reward [ ' color ' ] as Color ) . withOpacity ( 0.8 ) : reward [ ' color ' ] ;
return Container (
margin: const EdgeInsets . only ( bottom: 10 ) ,
padding: const EdgeInsets . all ( 12 ) ,
2026-03-20 14:00:00 +01:00
decoration: BoxDecoration (
2026-03-23 01:00:01 +01:00
color: rewardColor . withOpacity ( 0.1 ) ,
borderRadius: BorderRadius . circular ( 12 ) ,
border: Border . all ( color: rewardColor . withOpacity ( 0.5 ) , width: 1.5 ) ,
2026-03-20 14:00:00 +01:00
) ,
child: Row (
2026-03-23 01:00:01 +01:00
children: [
Container (
padding: const EdgeInsets . all ( 8 ) ,
decoration: BoxDecoration (
color: rewardColor . withOpacity ( 0.2 ) ,
shape: BoxShape . circle ,
) ,
child: Icon ( reward [ ' icon ' ] , color: rewardColor , size: 28 ) ,
) ,
const SizedBox ( width: 15 ) ,
Expanded (
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Text ( reward [ ' title ' ] , style: getSharedTextStyle ( themeType , TextStyle ( color: rewardColor , fontWeight: FontWeight . w900 , fontSize: 16 ) ) ) ,
const SizedBox ( height: 4 ) ,
Text ( reward [ ' desc ' ] , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? inkColor . withOpacity ( 0.9 ) : theme . text . withOpacity ( 0.9 ) , fontSize: 12 , height: 1.3 ) ) ) ,
] ,
)
)
]
2026-03-20 14:00:00 +01:00
) ,
2026-03-23 01:00:01 +01:00
) ;
} ) ,
] ,
2026-02-27 23:35:54 +01:00
2026-03-23 01:00:01 +01:00
const SizedBox ( height: 30 ) ,
// --- BOTTONI AZIONE ---
2026-02-27 23:35:54 +01:00
Column (
crossAxisAlignment: CrossAxisAlignment . stretch ,
children: [
if ( playerBeatCPU )
2026-03-23 01:00:01 +01:00
_buildPrimaryButton (
" PROSSIMO LIVELLO ➔ " ,
winnerColor ,
themeType ,
inkColor ,
( ) {
2026-02-27 23:35:54 +01:00
Navigator . pop ( context ) ;
game . increaseLevelAndRestart ( ) ;
} ,
)
else if ( game . isOnline )
2026-03-23 01:00:01 +01:00
_buildPrimaryButton (
game . opponentWantsRematch ? " ACCETTA RIVINCITA " : " CHIEDI RIVINCITA " ,
game . rematchRequested ? Colors . grey : ( winnerColor = = ( themeType = = AppThemeType . doodle ? inkColor : theme . text ) ? theme . playerBlue : winnerColor ) ,
themeType ,
inkColor ,
game . rematchRequested ? ( ) { } : ( ) = > game . requestRematch ( ) ,
2026-02-27 23:35:54 +01:00
)
else
2026-03-23 01:00:01 +01:00
_buildPrimaryButton (
" RIGIOCA " ,
winnerColor = = ( themeType = = AppThemeType . doodle ? inkColor : theme . text ) ? theme . playerBlue : winnerColor ,
themeType ,
inkColor ,
( ) {
2026-02-27 23:35:54 +01:00
Navigator . pop ( context ) ;
game . startNewGame ( game . board . radius , vsCPU: game . isVsCPU ) ;
} ,
) ,
const SizedBox ( height: 12 ) ,
2026-03-23 01:00:01 +01:00
_buildSecondaryButton (
" TORNA AL MENU " ,
themeType ,
inkColor ,
theme ,
( ) {
2026-02-27 23:35:54 +01:00
if ( game . isOnline ) {
game . disconnectOnlineGame ( ) ;
}
Navigator . pop ( context ) ;
Navigator . pop ( context ) ;
} ,
) ,
] ,
)
] ,
) ;
2026-03-23 01:00:01 +01:00
if ( themeType = = AppThemeType . doodle ) {
dialogContent = Transform . rotate (
angle: 0.015 ,
child: CustomPaint (
painter: DoodleBackgroundPainter ( fillColor: Colors . white . withOpacity ( 0.95 ) , strokeColor: inkColor , seed: 500 ) ,
child: Padding (
padding: const EdgeInsets . all ( 25.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " FINE PARTITA " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 22 , fontWeight: FontWeight . w900 , color: inkColor , letterSpacing: 2 ) ) ) ,
const SizedBox ( height: 20 ) ,
dialogContent ,
] ,
) ,
) ,
) ,
) ;
} else {
dialogContent = Container (
padding: const EdgeInsets . all ( 25.0 ) ,
decoration: BoxDecoration (
color: theme . background ,
borderRadius: BorderRadius . circular ( 20 ) ,
border: Border . all ( color: winnerColor . withOpacity ( 0.5 ) , width: 2 ) ,
) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " FINE PARTITA " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 22 , fontWeight: FontWeight . bold , color: theme . text ) ) ) ,
const SizedBox ( height: 20 ) ,
dialogContent ,
] ,
) ,
) ;
}
return Dialog (
backgroundColor: Colors . transparent ,
insetPadding: const EdgeInsets . symmetric ( horizontal: 20 , vertical: 20 ) ,
child: dialogContent ,
) ;
}
Widget _buildPrimaryButton ( String label , Color color , AppThemeType themeType , Color inkColor , VoidCallback onTap ) {
if ( themeType = = AppThemeType . doodle ) {
return GestureDetector (
onTap: onTap ,
child: CustomPaint (
painter: DoodleBackgroundPainter ( fillColor: color , strokeColor: inkColor , seed: label . length * 7 ) ,
child: Container (
height: 55 ,
alignment: Alignment . center ,
child: Text ( label , style: getSharedTextStyle ( themeType , const TextStyle ( fontSize: 16 , fontWeight: FontWeight . w900 , color: Colors . white , letterSpacing: 1.5 ) ) ) ,
) ,
) ,
) ;
}
return ElevatedButton (
style: ElevatedButton . styleFrom (
backgroundColor: color ,
foregroundColor: Colors . white ,
padding: const EdgeInsets . symmetric ( vertical: 15 ) ,
shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ,
elevation: 5 ,
) ,
onPressed: onTap ,
child: Text ( label , style: getSharedTextStyle ( themeType , const TextStyle ( fontWeight: FontWeight . bold , fontSize: 16 , letterSpacing: 1.5 ) ) ) ,
) ;
}
Widget _buildSecondaryButton ( String label , AppThemeType themeType , Color inkColor , ThemeColors theme , VoidCallback onTap ) {
if ( themeType = = AppThemeType . doodle ) {
return GestureDetector (
onTap: onTap ,
child: CustomPaint (
painter: DoodleBackgroundPainter ( fillColor: Colors . transparent , strokeColor: inkColor . withOpacity ( 0.5 ) , seed: label . length * 3 ) ,
child: Container (
height: 55 ,
alignment: Alignment . center ,
child: Text ( label , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor , letterSpacing: 1.5 ) ) ) ,
) ,
) ,
) ;
}
return OutlinedButton (
style: OutlinedButton . styleFrom (
foregroundColor: theme . text ,
side: BorderSide ( color: theme . text . withOpacity ( 0.3 ) , width: 2 ) ,
padding: const EdgeInsets . symmetric ( vertical: 15 ) ,
shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ,
) ,
onPressed: onTap ,
child: Text ( label , style: getSharedTextStyle ( themeType , TextStyle ( fontWeight: FontWeight . bold , color: theme . text , fontSize: 14 , letterSpacing: 1.5 ) ) ) ,
) ;
2026-02-27 23:35:54 +01:00
}
}