2026-03-20 14:00:00 +01:00
// ===========================================================================
// FILE: lib/ui/home/home_modals.dart
// ===========================================================================
import ' dart:ui ' ;
import ' package:flutter/material.dart ' ;
import ' package:provider/provider.dart ' ;
import ' package:cloud_firestore/cloud_firestore.dart ' ;
import ' package:firebase_auth/firebase_auth.dart ' ;
import ' package:shared_preferences/shared_preferences.dart ' ;
import ' ../../core/theme_manager.dart ' ;
import ' ../../core/app_colors.dart ' ;
import ' ../../logic/game_controller.dart ' ;
import ' ../../models/game_board.dart ' ;
import ' ../../services/storage_service.dart ' ;
import ' ../../services/multiplayer_service.dart ' ;
import ' ../../l10n/app_localizations.dart ' ;
import ' ../../widgets/painters.dart ' ;
import ' ../../widgets/cyber_border.dart ' ;
import ' ../game/game_screen.dart ' ;
import ' ../multiplayer/lobby_screen.dart ' ;
import ' ../multiplayer/lobby_widgets.dart ' ;
class HomeModals {
static void showNameDialog ( BuildContext context , VoidCallback onSuccess ) {
final TextEditingController nameController = TextEditingController ( text: StorageService . instance . playerName ) ;
final TextEditingController passController = TextEditingController ( ) ;
bool isLoadingAuth = false ;
bool obscurePassword = true ;
String errorMessage = " " ;
showDialog (
2026-03-20 19:00:01 +01:00
context: context ,
2026-03-20 22:00:01 +01:00
barrierDismissible: false ,
2026-03-20 19:00:01 +01:00
barrierColor: Colors . black . withOpacity ( 0.8 ) ,
2026-03-20 14:00:00 +01:00
builder: ( dialogContext ) {
final themeManager = dialogContext . watch < ThemeManager > ( ) ;
final themeType = themeManager . currentThemeType ;
Color inkColor = const Color ( 0xFF111122 ) ;
final loc = AppLocalizations . of ( dialogContext ) ! ;
return StatefulBuilder (
builder: ( context , setStateDialog ) {
Future < void > handleAuth ( bool isLogin ) async {
final name = nameController . text . trim ( ) ;
final password = passController . text . trim ( ) ;
setStateDialog ( ( ) { errorMessage = " " ; isLoadingAuth = true ; } ) ;
if ( name . isEmpty | | password . isEmpty ) {
setStateDialog ( ( ) { errorMessage = " Inserisci Nome e Password! " ; isLoadingAuth = false ; } ) ;
return ;
}
if ( password . length < 6 ) {
setStateDialog ( ( ) { errorMessage = " La password deve avere almeno 6 caratteri! " ; isLoadingAuth = false ; } ) ;
return ;
}
final fakeEmail = " ${ name . toLowerCase ( ) . replaceAll ( ' ' , ' ' ) } @tetraq.game " ;
try {
if ( isLogin ) {
await FirebaseAuth . instance . signInWithEmailAndPassword ( email: fakeEmail , password: password ) ;
final doc = await FirebaseFirestore . instance . collection ( ' leaderboard ' ) . doc ( FirebaseAuth . instance . currentUser ! . uid ) . get ( ) ;
if ( doc . exists ) {
final data = doc . data ( ) as Map < String , dynamic > ;
final prefs = await SharedPreferences . getInstance ( ) ;
await prefs . setInt ( ' totalXP ' , data [ ' xp ' ] ? ? 0 ) ;
await prefs . setInt ( ' wins ' , data [ ' wins ' ] ? ? 0 ) ;
await prefs . setInt ( ' losses ' , data [ ' losses ' ] ? ? 0 ) ;
}
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( " Bentornato $ name ! Dati sincronizzati. " ) , backgroundColor: Colors . green ) ) ;
} else {
await FirebaseAuth . instance . createUserWithEmailAndPassword ( email: fakeEmail , password: password ) ;
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( " Account creato con successo! " ) , backgroundColor: Colors . green ) ) ;
}
await StorageService . instance . savePlayerName ( name ) ;
if ( context . mounted ) Navigator . of ( dialogContext ) . pop ( ) ;
onSuccess ( ) ;
} on FirebaseAuthException catch ( e ) {
String msg = " Errore di autenticazione. " ;
if ( e . code = = ' email-already-in-use ' ) msg = " Nome occupato! \n Se è il tuo account, clicca su ACCEDI. " ;
else if ( e . code = = ' user-not-found ' | | e . code = = ' wrong-password ' | | e . code = = ' invalid-credential ' ) msg = " Nome o Password errati! " ;
setStateDialog ( ( ) { errorMessage = msg ; isLoadingAuth = false ; } ) ;
} catch ( e ) {
setStateDialog ( ( ) { errorMessage = " Errore imprevisto: $ e " ; isLoadingAuth = false ; } ) ;
}
}
Widget dialogContent = themeType = = AppThemeType . doodle
? CustomPaint (
painter: DoodleBackgroundPainter ( fillColor: Colors . yellow . shade100 , strokeColor: inkColor , seed: 100 ) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . symmetric ( vertical: 20.0 , horizontal: 20.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( loc . welcomeTitle , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontWeight: FontWeight . w900 , fontSize: 24 , letterSpacing: 2.0 ) ) , textAlign: TextAlign . center ) ,
const SizedBox ( height: 10 ) ,
2026-03-20 19:00:01 +01:00
Text ( ' Scegli una Password per il Cloud. \n I tuoi XP e il tuo Livello saranno protetti e non li perderai mai! ' , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor . withOpacity ( 0.8 ) , fontSize: 13 , fontWeight: FontWeight . bold ) ) , textAlign: TextAlign . center ) ,
2026-03-20 14:00:00 +01:00
const SizedBox ( height: 15 ) ,
TextField (
controller: nameController , textCapitalization: TextCapitalization . characters , textAlign: TextAlign . center , maxLength: 8 ,
style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 24 , fontWeight: FontWeight . bold , letterSpacing: 4 ) ) ,
decoration: InputDecoration (
hintText: loc . nameHint , hintStyle: getSharedTextStyle ( themeType , TextStyle ( color: inkColor . withOpacity ( 0.3 ) , letterSpacing: 4 ) ) ,
filled: false , counterText: " " ,
enabledBorder: UnderlineInputBorder ( borderSide: BorderSide ( color: inkColor , width: 3 ) ) ,
focusedBorder: UnderlineInputBorder ( borderSide: BorderSide ( color: Colors . red . shade200 , width: 5 ) )
) ,
) ,
const SizedBox ( height: 8 ) ,
TextField (
controller: passController , obscureText: obscurePassword , textAlign: TextAlign . center , maxLength: 20 ,
style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 20 , fontWeight: FontWeight . bold , letterSpacing: 8 ) ) ,
decoration: InputDecoration (
hintText: " PASSWORD " , hintStyle: getSharedTextStyle ( themeType , TextStyle ( color: inkColor . withOpacity ( 0.3 ) , letterSpacing: 4 ) ) ,
filled: false , counterText: " " ,
enabledBorder: UnderlineInputBorder ( borderSide: BorderSide ( color: inkColor , width: 3 ) ) ,
focusedBorder: UnderlineInputBorder ( borderSide: BorderSide ( color: Colors . red . shade200 , width: 5 ) ) ,
suffixIcon: IconButton (
icon: Icon ( obscurePassword ? Icons . visibility : Icons . visibility_off , color: inkColor . withOpacity ( 0.6 ) ) ,
onPressed: ( ) { setStateDialog ( ( ) { obscurePassword = ! obscurePassword ; } ) ; } ,
) ,
) ,
) ,
const SizedBox ( height: 15 ) ,
if ( errorMessage . isNotEmpty )
Padding ( padding: const EdgeInsets . only ( bottom: 10 ) , child: Text ( errorMessage , style: getSharedTextStyle ( themeType , const TextStyle ( color: Colors . red , fontSize: 14 , fontWeight: FontWeight . bold ) ) , textAlign: TextAlign . center ) ) ,
2026-03-20 19:00:01 +01:00
Text ( " 💡 Usa una password facile da ricordare! " , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor . withOpacity ( 0.6 ) , fontSize: 11 , height: 1.3 ) ) , textAlign: TextAlign . center ) ,
2026-03-20 14:00:00 +01:00
const SizedBox ( height: 15 ) ,
isLoadingAuth ? CircularProgressIndicator ( color: inkColor ) : Row (
children: [
Expanded ( child: GestureDetector ( onTap: ( ) = > handleAuth ( true ) , child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: Colors . blue . shade200 , strokeColor: inkColor , seed: 101 ) , child: Container ( height: 45 , alignment: Alignment . center , child: Text ( " ACCEDI " , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 14 , fontWeight: FontWeight . bold , letterSpacing: 1.5 ) ) ) ) ) ) ) ,
const SizedBox ( width: 10 ) ,
Expanded ( child: GestureDetector ( onTap: ( ) = > handleAuth ( false ) , child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: Colors . green . shade200 , strokeColor: inkColor , seed: 102 ) , child: Container ( height: 45 , alignment: Alignment . center , child: Text ( " REGISTRATI " , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 14 , fontWeight: FontWeight . bold , letterSpacing: 1.5 ) ) ) ) ) ) ) ,
] ,
) ,
] ,
) ,
) ,
) ,
)
: Container (
decoration: BoxDecoration ( color: themeManager . currentColors . background , borderRadius: BorderRadius . circular ( 25 ) , border: Border . all ( color: themeManager . currentColors . playerBlue . withOpacity ( 0.5 ) , width: 2 ) , boxShadow: [ BoxShadow ( color: themeManager . currentColors . playerBlue . withOpacity ( 0.3 ) , blurRadius: 20 , spreadRadius: 5 ) ] ) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . symmetric ( vertical: 20.0 , horizontal: 20.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( loc . welcomeTitle , style: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text , fontWeight: FontWeight . w900 , fontSize: 20 , letterSpacing: 1.5 ) ) , textAlign: TextAlign . center ) ,
const SizedBox ( height: 10 ) ,
2026-03-20 19:00:01 +01:00
Text ( ' Scegli una Password per il Cloud. \n I tuoi XP e il tuo Livello saranno protetti e non li perderai mai! ' , style: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text . withOpacity ( 0.8 ) , fontSize: 13 , fontWeight: FontWeight . bold ) ) , textAlign: TextAlign . center ) ,
2026-03-20 14:00:00 +01:00
const SizedBox ( height: 15 ) ,
TextField (
controller: nameController , textCapitalization: TextCapitalization . characters , textAlign: TextAlign . center , maxLength: 8 ,
style: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text , fontSize: 24 , fontWeight: FontWeight . bold , letterSpacing: 4 ) ) ,
decoration: InputDecoration (
hintText: loc . nameHint , hintStyle: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text . withOpacity ( 0.3 ) , letterSpacing: 4 ) ) ,
filled: true , fillColor: themeManager . currentColors . text . withOpacity ( 0.05 ) , counterText: " " ,
enabledBorder: OutlineInputBorder ( borderSide: BorderSide ( color: themeManager . currentColors . gridLine . withOpacity ( 0.5 ) , width: 2 ) , borderRadius: BorderRadius . circular ( 15 ) ) ,
focusedBorder: OutlineInputBorder ( borderSide: BorderSide ( color: themeManager . currentColors . playerBlue , width: 3 ) , borderRadius: BorderRadius . circular ( 15 ) )
) ,
) ,
const SizedBox ( height: 10 ) ,
TextField (
controller: passController , obscureText: obscurePassword , textAlign: TextAlign . center , maxLength: 20 ,
style: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text , fontSize: 20 , fontWeight: FontWeight . bold , letterSpacing: 8 ) ) ,
decoration: InputDecoration (
hintText: " PASSWORD " , hintStyle: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text . withOpacity ( 0.3 ) , letterSpacing: 4 ) ) ,
filled: true , fillColor: themeManager . currentColors . text . withOpacity ( 0.05 ) , counterText: " " ,
enabledBorder: OutlineInputBorder ( borderSide: BorderSide ( color: themeManager . currentColors . gridLine . withOpacity ( 0.5 ) , width: 2 ) , borderRadius: BorderRadius . circular ( 15 ) ) ,
focusedBorder: OutlineInputBorder ( borderSide: BorderSide ( color: themeManager . currentColors . playerBlue , width: 3 ) , borderRadius: BorderRadius . circular ( 15 ) ) ,
suffixIcon: IconButton (
icon: Icon ( obscurePassword ? Icons . visibility : Icons . visibility_off , color: themeManager . currentColors . text . withOpacity ( 0.6 ) ) ,
onPressed: ( ) { setStateDialog ( ( ) { obscurePassword = ! obscurePassword ; } ) ; } ,
) ,
) ,
) ,
const SizedBox ( height: 15 ) ,
if ( errorMessage . isNotEmpty )
Padding ( padding: const EdgeInsets . only ( bottom: 10 ) , child: Text ( errorMessage , style: getSharedTextStyle ( themeType , const TextStyle ( color: Colors . redAccent , fontSize: 14 , fontWeight: FontWeight . bold ) ) , textAlign: TextAlign . center ) ) ,
2026-03-20 19:00:01 +01:00
Text ( " 💡 Usa una password facile da ricordare! " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeManager . currentColors . text . withOpacity ( 0.6 ) , fontSize: 11 , height: 1.3 ) ) , textAlign: TextAlign . center ) ,
2026-03-20 14:00:00 +01:00
const SizedBox ( height: 20 ) ,
isLoadingAuth ? CircularProgressIndicator ( color: themeManager . currentColors . playerBlue ) : Row (
children: [
Expanded ( child: SizedBox ( height: 45 , child: ElevatedButton ( style: ElevatedButton . styleFrom ( backgroundColor: themeManager . currentColors . text . withOpacity ( 0.1 ) , foregroundColor: themeManager . currentColors . text , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) , side: BorderSide ( color: themeManager . currentColors . playerBlue , width: 1.5 ) ) , onPressed: ( ) = > handleAuth ( true ) , child: Text ( " ACCEDI " , style: getSharedTextStyle ( themeType , const TextStyle ( fontSize: 13 , fontWeight: FontWeight . bold , letterSpacing: 1.0 ) ) ) ) ) ) ,
const SizedBox ( width: 10 ) ,
Expanded ( child: SizedBox ( height: 45 , child: ElevatedButton ( style: ElevatedButton . styleFrom ( backgroundColor: themeManager . currentColors . playerBlue , foregroundColor: Colors . white , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ) , onPressed: ( ) = > handleAuth ( false ) , child: Text ( " REGISTRATI " , style: getSharedTextStyle ( themeType , const TextStyle ( fontSize: 13 , fontWeight: FontWeight . bold , letterSpacing: 1.0 ) ) ) ) ) ) ,
] ,
) ,
] ,
) ,
) ,
) ,
) ;
if ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) dialogContent = AnimatedCyberBorder ( child: dialogContent ) ;
2026-03-20 19:00:01 +01:00
return PopScope (
canPop: false ,
child: Dialog ( backgroundColor: Colors . transparent , insetPadding: const EdgeInsets . all ( 20 ) , child: dialogContent )
) ;
2026-03-20 14:00:00 +01:00
} ,
) ;
} ,
) ;
}
2026-03-20 22:00:01 +01:00
// --- SELETTORE DEL TEMPO A 3 OPZIONI ---
static Widget _buildTimeOption ( String label , String sub , String value , String current , ThemeColors theme , AppThemeType type , VoidCallback onTap ) {
bool isSel = value = = current ;
return Expanded (
child: GestureDetector (
onTap: onTap ,
child: Container (
margin: const EdgeInsets . symmetric ( horizontal: 4 ) ,
height: 50 ,
decoration: BoxDecoration (
color: isSel ? Colors . orange . shade600 : ( type = = AppThemeType . doodle ? Colors . white : theme . text . withOpacity ( 0.05 ) ) ,
borderRadius: BorderRadius . circular ( 12 ) ,
border: Border . all ( color: isSel ? Colors . orange . shade800 : ( type = = AppThemeType . doodle ? const Color ( 0xFF111122 ) : Colors . white24 ) , width: isSel ? 2 : 1.5 ) ,
boxShadow: isSel & & type ! = AppThemeType . doodle ? [ BoxShadow ( color: Colors . orange . withOpacity ( 0.5 ) , blurRadius: 8 ) ] : [ ] ,
) ,
child: Column (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
Text ( label , style: getSharedTextStyle ( type , TextStyle ( color: isSel ? Colors . white : ( type = = AppThemeType . doodle ? const Color ( 0xFF111122 ) : theme . text ) , fontWeight: FontWeight . w900 , fontSize: 13 ) ) ) ,
if ( sub . isNotEmpty ) Text ( sub , style: getSharedTextStyle ( type , TextStyle ( color: isSel ? Colors . white70 : ( type = = AppThemeType . doodle ? Colors . black54 : theme . text . withOpacity ( 0.5 ) ) , fontWeight: FontWeight . bold , fontSize: 8 ) ) ) ,
] ,
) ,
) ,
) ,
) ;
}
static void showChallengeSetupDialog ( BuildContext context , String targetName , Function ( int radius , ArenaShape shape , String timeMode ) onStart ) {
int localRadius = 4 ; ArenaShape localShape = ArenaShape . classic ; String localTimeMode = ' fixed ' ;
bool isChaosUnlocked = StorageService . instance . playerLevel > = 7 ;
showDialog (
context: context , barrierColor: Colors . black . withOpacity ( 0.8 ) ,
builder: ( ctx ) {
final themeManager = ctx . watch < ThemeManager > ( ) ;
final theme = themeManager . currentColors ; final themeType = themeManager . currentThemeType ;
Color inkColor = const Color ( 0xFF111122 ) ;
return StatefulBuilder (
builder: ( context , setStateDialog ) {
Widget dialogContent = themeType = = AppThemeType . doodle
? Transform . rotate (
angle: 0.015 ,
child: CustomPaint (
painter: DoodleBackgroundPainter ( fillColor: Colors . white . withOpacity ( 0.95 ) , strokeColor: inkColor , seed: 200 ) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . all ( 25.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " SFIDA $ targetName " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 26 , fontWeight: FontWeight . w900 , color: theme . playerRed , letterSpacing: 2 ) ) ) ,
const SizedBox ( height: 10 ) ,
Text ( " IMPOSTAZIONI STANZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . bold , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) ,
const SizedBox ( height: 25 ) ,
Text ( " FORMA ARENA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 15 ) ,
Wrap (
spacing: 12 , runSpacing: 12 , alignment: WrapAlignment . center ,
children: [
NeonShapeButton ( icon: Icons . diamond_outlined , label: ' Rombo ' , isSelected: localShape = = ArenaShape . classic , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . classic ) ) ,
NeonShapeButton ( icon: Icons . add , label: ' Croce ' , isSelected: localShape = = ArenaShape . cross , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . cross ) ) ,
NeonShapeButton ( icon: Icons . donut_large , label: ' Buco ' , isSelected: localShape = = ArenaShape . donut , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . donut ) ) ,
NeonShapeButton ( icon: Icons . hourglass_bottom , label: ' Clessidra ' , isSelected: localShape = = ArenaShape . hourglass , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . hourglass ) ) ,
NeonShapeButton ( icon: Icons . all_inclusive , label: ' Caos ' , isSelected: localShape = = ArenaShape . chaos , theme: theme , themeType: themeType , isSpecial: true , isLocked: ! isChaosUnlocked , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . chaos ) ) ,
] ,
) ,
const SizedBox ( height: 25 ) , Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
Text ( " GRANDEZZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 15 ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceEvenly ,
children: [
NeonSizeButton ( label: ' S ' , isSelected: localRadius = = 3 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 3 ) ) ,
NeonSizeButton ( label: ' M ' , isSelected: localRadius = = 4 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 4 ) ) ,
NeonSizeButton ( label: ' L ' , isSelected: localRadius = = 5 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 5 ) ) ,
NeonSizeButton ( label: ' MAX ' , isSelected: localRadius = = 6 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 6 ) ) ,
] ,
) ,
const SizedBox ( height: 25 ) , Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
Text ( " TEMPO E OPZIONI " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Row (
children: [
_buildTimeOption ( ' 10s ' , ' FISSO ' , ' fixed ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' fixed ' ) ) ,
_buildTimeOption ( ' RELAX ' , ' INFINITO ' , ' relax ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' relax ' ) ) ,
_buildTimeOption ( ' DINAMICO ' , ' -2s A PARTITA ' , ' dynamic ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' dynamic ' ) ) ,
] ,
) ,
const SizedBox ( height: 35 ) ,
Row (
children: [
Expanded (
child: GestureDetector (
onTap: ( ) {
Navigator . pop ( ctx ) ;
onStart ( localRadius , localShape , localTimeMode ) ;
} ,
child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: theme . playerRed , strokeColor: inkColor , seed: 300 ) , child: Container ( height: 55 , alignment: Alignment . center , child: Text ( " AVVIA " , style: getSharedTextStyle ( themeType , const TextStyle ( fontSize: 18 , fontWeight: FontWeight . w900 , letterSpacing: 2.0 , color: Colors . white ) ) ) ) ) ,
) ,
) ,
const SizedBox ( width: 15 ) ,
Expanded (
child: GestureDetector (
onTap: ( ) = > Navigator . pop ( ctx ) ,
child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: Colors . grey . shade400 , strokeColor: inkColor , seed: 301 ) , child: Container ( height: 55 , alignment: Alignment . center , child: Text ( " ANNULLA " , style: getSharedTextStyle ( themeType , const TextStyle ( fontSize: 18 , fontWeight: FontWeight . w900 , letterSpacing: 2.0 , color: Colors . white ) ) ) ) ) ,
) ,
) ,
] ,
)
] ,
) ,
) ,
) ,
) ,
)
: Container (
decoration: BoxDecoration (
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: [ theme . background . withOpacity ( 0.95 ) , theme . background . withOpacity ( 0.8 ) ] ) ,
borderRadius: BorderRadius . circular ( 25 ) , border: themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . arcade | | themeType = = AppThemeType . music ? null : Border . all ( color: Colors . white . withOpacity ( 0.15 ) , width: 1.5 ) ,
boxShadow: themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . arcade | | themeType = = AppThemeType . music ? [ ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.5 ) , blurRadius: 20 , offset: const Offset ( 4 , 10 ) ) ] ,
) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . all ( 20.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " SFIDA $ targetName " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 24 , fontWeight: FontWeight . w900 , color: theme . playerRed , letterSpacing: 2 ) ) ) ,
const SizedBox ( height: 10 ) ,
Text ( " IMPOSTAZIONI STANZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . bold , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) ,
const SizedBox ( height: 20 ) ,
Text ( " FORMA ARENA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Wrap (
spacing: 10 , runSpacing: 10 , alignment: WrapAlignment . center ,
children: [
NeonShapeButton ( icon: Icons . diamond_outlined , label: ' Rombo ' , isSelected: localShape = = ArenaShape . classic , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . classic ) ) ,
NeonShapeButton ( icon: Icons . add , label: ' Croce ' , isSelected: localShape = = ArenaShape . cross , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . cross ) ) ,
NeonShapeButton ( icon: Icons . donut_large , label: ' Buco ' , isSelected: localShape = = ArenaShape . donut , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . donut ) ) ,
NeonShapeButton ( icon: Icons . hourglass_bottom , label: ' Clessidra ' , isSelected: localShape = = ArenaShape . hourglass , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . hourglass ) ) ,
NeonShapeButton ( icon: Icons . all_inclusive , label: ' Caos ' , isSelected: localShape = = ArenaShape . chaos , theme: theme , themeType: themeType , isSpecial: true , isLocked: ! isChaosUnlocked , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . chaos ) ) ,
] ,
) ,
const SizedBox ( height: 20 ) , Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
Text ( " GRANDEZZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceEvenly ,
children: [
NeonSizeButton ( label: ' S ' , isSelected: localRadius = = 3 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 3 ) ) ,
NeonSizeButton ( label: ' M ' , isSelected: localRadius = = 4 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 4 ) ) ,
NeonSizeButton ( label: ' L ' , isSelected: localRadius = = 5 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 5 ) ) ,
NeonSizeButton ( label: ' MAX ' , isSelected: localRadius = = 6 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 6 ) ) ,
] ,
) ,
const SizedBox ( height: 20 ) , Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
Text ( " TEMPO E OPZIONI " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Row (
children: [
_buildTimeOption ( ' 10s ' , ' FISSO ' , ' fixed ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' fixed ' ) ) ,
_buildTimeOption ( ' RELAX ' , ' INFINITO ' , ' relax ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' relax ' ) ) ,
_buildTimeOption ( ' DINAMICO ' , ' -2s A PARTITA ' , ' dynamic ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' dynamic ' ) ) ,
] ,
) ,
const SizedBox ( height: 30 ) ,
Row (
children: [
Expanded (
child: SizedBox (
height: 55 ,
child: ElevatedButton (
style: ElevatedButton . styleFrom ( backgroundColor: theme . playerRed , foregroundColor: Colors . white , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ) ,
onPressed: ( ) {
Navigator . pop ( ctx ) ;
onStart ( localRadius , localShape , localTimeMode ) ;
} ,
child: const Text ( " AVVIA " , style: TextStyle ( fontSize: 16 , fontWeight: FontWeight . w900 , letterSpacing: 2 ) ) ,
) ,
) ,
) ,
const SizedBox ( width: 15 ) ,
Expanded (
child: SizedBox (
height: 55 ,
child: ElevatedButton (
style: ElevatedButton . styleFrom ( backgroundColor: Colors . grey . shade800 , foregroundColor: Colors . white , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ) ,
onPressed: ( ) = > Navigator . pop ( ctx ) ,
child: const Text ( " ANNULLA " , style: TextStyle ( fontSize: 16 , fontWeight: FontWeight . w900 , letterSpacing: 2 ) ) ,
) ,
) ,
) ,
] ,
)
] ,
) ,
) ,
) ,
) ;
if ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) {
dialogContent = AnimatedCyberBorder ( child: dialogContent ) ;
}
return Dialog ( backgroundColor: Colors . transparent , insetPadding: const EdgeInsets . symmetric ( horizontal: 15 , vertical: 20 ) , child: dialogContent ) ;
} ,
) ;
}
) ;
}
2026-03-20 14:00:00 +01:00
static void showMatchSetupDialog ( BuildContext context , bool isVsCPU ) {
2026-03-20 22:00:01 +01:00
int localRadius = 4 ; ArenaShape localShape = ArenaShape . classic ; String localTimeMode = ' fixed ' ;
2026-03-20 14:00:00 +01:00
bool isChaosUnlocked = StorageService . instance . playerLevel > = 7 ;
final loc = AppLocalizations . of ( context ) ! ;
showDialog (
context: context , barrierColor: Colors . black . withOpacity ( 0.8 ) ,
builder: ( ctx ) {
final themeManager = ctx . watch < ThemeManager > ( ) ;
final theme = themeManager . currentColors ; final themeType = themeManager . currentThemeType ;
Color inkColor = const Color ( 0xFF111122 ) ;
return StatefulBuilder (
builder: ( context , setStateDialog ) {
Widget dialogContent = themeType = = AppThemeType . doodle
? Transform . rotate (
angle: 0.015 ,
child: CustomPaint (
painter: DoodleBackgroundPainter ( fillColor: Colors . white . withOpacity ( 0.95 ) , strokeColor: inkColor , seed: 200 ) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . all ( 25.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Row ( children: [ SizedBox ( width: 40 , child: IconButton ( padding: EdgeInsets . zero , alignment: Alignment . centerLeft , icon: Icon ( Icons . arrow_back_ios_new , color: inkColor , size: 26 ) , onPressed: ( ) = > Navigator . pop ( ctx ) ) ) , Expanded ( child: Text ( isVsCPU ? loc . cpuTitle : loc . localTitle , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 26 , fontWeight: FontWeight . w900 , color: inkColor , letterSpacing: 2 ) ) ) ) , const SizedBox ( width: 40 ) ] ) ,
const SizedBox ( height: 25 ) ,
if ( isVsCPU ) . . . [
Icon ( Icons . smart_toy , size: 50 , color: inkColor . withOpacity ( 0.6 ) ) , const SizedBox ( height: 10 ) ,
Text ( " MODALITÀ CAMPAGNA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 16 , fontWeight: FontWeight . w900 , color: inkColor ) ) ) , const SizedBox ( height: 10 ) ,
2026-03-20 22:00:01 +01:00
Text ( " Livello CPU: ${ StorageService . instance . cpuLevel } \n Forma e tempo si adatteranno alla tua bravura! " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 13 , color: inkColor . withOpacity ( 0.8 ) , height: 1.4 ) ) ) , const SizedBox ( height: 25 ) ,
2026-03-20 14:00:00 +01:00
Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
] else . . . [
Text ( " FORMA ARENA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 15 ) ,
Wrap (
spacing: 12 , runSpacing: 12 , alignment: WrapAlignment . center ,
children: [
NeonShapeButton ( icon: Icons . diamond_outlined , label: ' Rombo ' , isSelected: localShape = = ArenaShape . classic , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . classic ) ) ,
NeonShapeButton ( icon: Icons . add , label: ' Croce ' , isSelected: localShape = = ArenaShape . cross , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . cross ) ) ,
NeonShapeButton ( icon: Icons . donut_large , label: ' Buco ' , isSelected: localShape = = ArenaShape . donut , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . donut ) ) ,
NeonShapeButton ( icon: Icons . hourglass_bottom , label: ' Clessidra ' , isSelected: localShape = = ArenaShape . hourglass , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . hourglass ) ) ,
NeonShapeButton ( icon: Icons . all_inclusive , label: ' Caos ' , isSelected: localShape = = ArenaShape . chaos , theme: theme , themeType: themeType , isSpecial: true , isLocked: ! isChaosUnlocked , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . chaos ) ) ,
] ,
) ,
const SizedBox ( height: 25 ) , Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
Text ( " GRANDEZZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 15 ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceEvenly ,
children: [
NeonSizeButton ( label: ' S ' , isSelected: localRadius = = 3 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 3 ) ) ,
NeonSizeButton ( label: ' M ' , isSelected: localRadius = = 4 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 4 ) ) ,
NeonSizeButton ( label: ' L ' , isSelected: localRadius = = 5 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 5 ) ) ,
NeonSizeButton ( label: ' MAX ' , isSelected: localRadius = = 6 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 6 ) ) ,
] ,
) ,
const SizedBox ( height: 25 ) , Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
2026-03-20 22:00:01 +01:00
// TEMPO È ORA ESCLUSIVO PER IL MULTIPLAYER
Text ( " TEMPO " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Row (
children: [
_buildTimeOption ( ' 10s ' , ' FISSO ' , ' fixed ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' fixed ' ) ) ,
_buildTimeOption ( ' RELAX ' , ' INFINITO ' , ' relax ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' relax ' ) ) ,
_buildTimeOption ( ' DINAMICO ' , ' -2s A PARTITA ' , ' dynamic ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' dynamic ' ) ) ,
] ,
) , const SizedBox ( height: 35 ) ,
] ,
2026-03-20 14:00:00 +01:00
Transform . rotate (
angle: - 0.02 ,
child: GestureDetector (
2026-03-20 19:00:01 +01:00
onTap: ( ) { Navigator . pop ( ctx ) ; context . read < GameController > ( ) . startNewGame ( localRadius , vsCPU: isVsCPU , shape: localShape , timeMode: localTimeMode ) ; Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const GameScreen ( ) ) ) ; } ,
2026-03-20 14:00:00 +01:00
child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: Colors . green . shade200 , strokeColor: inkColor , seed: 300 ) , child: Container ( height: 65 , width: double . infinity , alignment: Alignment . center , child: Text ( loc . startGame , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 22 , fontWeight: FontWeight . w900 , letterSpacing: 3.0 , color: inkColor ) ) ) ) ) ,
) ,
)
] ,
) ,
) ,
) ,
) ,
)
: Container (
decoration: BoxDecoration (
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: [ theme . background . withOpacity ( 0.95 ) , theme . background . withOpacity ( 0.8 ) ] ) ,
borderRadius: BorderRadius . circular ( 25 ) , border: themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . arcade | | themeType = = AppThemeType . music ? null : Border . all ( color: Colors . white . withOpacity ( 0.15 ) , width: 1.5 ) ,
boxShadow: themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . arcade | | themeType = = AppThemeType . music ? [ ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.5 ) , blurRadius: 20 , offset: const Offset ( 4 , 10 ) ) ] ,
) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . all ( 20.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Row ( children: [ SizedBox ( width: 40 , child: IconButton ( padding: EdgeInsets . zero , alignment: Alignment . centerLeft , icon: Icon ( Icons . arrow_back_ios_new , color: theme . text , size: 26 ) , onPressed: ( ) = > Navigator . pop ( ctx ) ) ) , Expanded ( child: Text ( isVsCPU ? loc . cpuTitle : loc . localTitle , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 24 , fontWeight: FontWeight . w900 , color: theme . text , letterSpacing: 2 ) ) ) ) , const SizedBox ( width: 40 ) ] ) ,
const SizedBox ( height: 20 ) ,
if ( isVsCPU ) . . . [
Icon ( Icons . smart_toy , size: 50 , color: theme . playerBlue ) , const SizedBox ( height: 10 ) ,
Text ( " MODALITÀ CAMPAGNA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 16 , fontWeight: FontWeight . w900 , color: theme . text , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
2026-03-20 22:00:01 +01:00
Text ( " Livello CPU: ${ StorageService . instance . cpuLevel } \n Forma e tempo si adatteranno alla tua bravura! " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 13 , color: theme . text . withOpacity ( 0.7 ) , height: 1.4 ) ) ) , const SizedBox ( height: 20 ) ,
2026-03-20 14:00:00 +01:00
Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
] else . . . [
Text ( " FORMA ARENA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Wrap (
spacing: 10 , runSpacing: 10 , alignment: WrapAlignment . center ,
children: [
NeonShapeButton ( icon: Icons . diamond_outlined , label: ' Rombo ' , isSelected: localShape = = ArenaShape . classic , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . classic ) ) ,
NeonShapeButton ( icon: Icons . add , label: ' Croce ' , isSelected: localShape = = ArenaShape . cross , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . cross ) ) ,
NeonShapeButton ( icon: Icons . donut_large , label: ' Buco ' , isSelected: localShape = = ArenaShape . donut , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . donut ) ) ,
NeonShapeButton ( icon: Icons . hourglass_bottom , label: ' Clessidra ' , isSelected: localShape = = ArenaShape . hourglass , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . hourglass ) ) ,
NeonShapeButton ( icon: Icons . all_inclusive , label: ' Caos ' , isSelected: localShape = = ArenaShape . chaos , theme: theme , themeType: themeType , isSpecial: true , isLocked: ! isChaosUnlocked , onTap: ( ) = > setStateDialog ( ( ) = > localShape = ArenaShape . chaos ) ) ,
] ,
) ,
const SizedBox ( height: 20 ) , Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
Text ( " GRANDEZZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceEvenly ,
children: [
NeonSizeButton ( label: ' S ' , isSelected: localRadius = = 3 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 3 ) ) ,
NeonSizeButton ( label: ' M ' , isSelected: localRadius = = 4 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 4 ) ) ,
NeonSizeButton ( label: ' L ' , isSelected: localRadius = = 5 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 5 ) ) ,
NeonSizeButton ( label: ' MAX ' , isSelected: localRadius = = 6 , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localRadius = 6 ) ) ,
] ,
) ,
const SizedBox ( height: 20 ) , Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
2026-03-20 22:00:01 +01:00
Text ( " TEMPO " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
Row (
children: [
_buildTimeOption ( ' 10s ' , ' FISSO ' , ' fixed ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' fixed ' ) ) ,
_buildTimeOption ( ' RELAX ' , ' INFINITO ' , ' relax ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' relax ' ) ) ,
_buildTimeOption ( ' DINAMICO ' , ' -2s A PARTITA ' , ' dynamic ' , localTimeMode , theme , themeType , ( ) = > setStateDialog ( ( ) = > localTimeMode = ' dynamic ' ) ) ,
] ,
) , const SizedBox ( height: 30 ) ,
] ,
2026-03-20 14:00:00 +01:00
SizedBox (
width: double . infinity , height: 60 ,
child: ElevatedButton (
style: ElevatedButton . styleFrom ( backgroundColor: isVsCPU ? Colors . purple . shade400 : theme . playerRed , foregroundColor: Colors . white , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 20 ) ) ) ,
2026-03-20 19:00:01 +01:00
onPressed: ( ) { Navigator . pop ( ctx ) ; context . read < GameController > ( ) . startNewGame ( localRadius , vsCPU: isVsCPU , shape: localShape , timeMode: localTimeMode ) ; Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const GameScreen ( ) ) ) ; } ,
2026-03-20 14:00:00 +01:00
child: Text ( loc . startGame , style: const TextStyle ( fontSize: 18 , fontWeight: FontWeight . w900 , letterSpacing: 2 ) ) ,
) ,
)
] ,
) ,
) ,
) ,
) ;
if ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) {
dialogContent = AnimatedCyberBorder ( child: dialogContent ) ;
}
return Dialog ( backgroundColor: Colors . transparent , insetPadding: const EdgeInsets . symmetric ( horizontal: 15 , vertical: 20 ) , child: dialogContent ) ;
} ,
) ;
}
) ;
}
static void showWaitingDialog ( {
required BuildContext context ,
required String code ,
required bool isPublicRoom ,
required int selectedRadius ,
required ArenaShape selectedShape ,
2026-03-20 22:00:01 +01:00
required String selectedTimeMode ,
2026-03-20 14:00:00 +01:00
required MultiplayerService multiplayerService ,
required VoidCallback onRoomStarted ,
required VoidCallback onCleanup ,
} ) {
showDialog (
context: context ,
barrierDismissible: false ,
builder: ( dialogContext ) {
final theme = dialogContext . watch < ThemeManager > ( ) . currentColors ;
final themeType = dialogContext . read < ThemeManager > ( ) . currentThemeType ;
Widget dialogContent = Column (
mainAxisSize: MainAxisSize . min ,
children: [
CircularProgressIndicator ( color: theme . playerRed ) , const SizedBox ( height: 25 ) ,
Text ( " CODICE STANZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 16 , fontWeight: FontWeight . bold , color: theme . text . withOpacity ( 0.6 ) , letterSpacing: 2 ) ) ) ,
Text ( code , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 40 , fontWeight: FontWeight . w900 , color: theme . playerRed , letterSpacing: 8 , shadows: themeType = = AppThemeType . doodle ? [ ] : [ Shadow ( color: theme . playerRed . withOpacity ( 0.5 ) , blurRadius: 10 ) ] ) ) ) ,
const SizedBox ( height: 25 ) ,
Transform . rotate (
angle: themeType = = AppThemeType . doodle ? 0.02 : 0 ,
child: Container (
padding: const EdgeInsets . all ( 18 ) ,
decoration: BoxDecoration (
color: themeType = = AppThemeType . doodle ? Colors . white : theme . text . withOpacity ( 0.05 ) ,
borderRadius: BorderRadius . circular ( 20 ) ,
border: Border . all ( color: themeType = = AppThemeType . doodle ? theme . text : theme . playerBlue . withOpacity ( 0.3 ) , width: themeType = = AppThemeType . doodle ? 2 : 1.5 ) ,
boxShadow: themeType = = AppThemeType . doodle
? [ BoxShadow ( color: theme . text . withOpacity ( 0.8 ) , offset: const Offset ( 4 , 4 ) ) ]
: [ BoxShadow ( color: theme . playerBlue . withOpacity ( 0.1 ) , blurRadius: 10 ) ]
) ,
child: Column (
children: [
Icon ( isPublicRoom ? Icons . podcasts : Icons . share , color: theme . playerBlue , size: 32 ) , const SizedBox ( height: 12 ) ,
2026-03-20 22:00:01 +01:00
Text ( isPublicRoom ? " Sei in Bacheca! " : " Invito inviato " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( color: theme . text , fontWeight: FontWeight . w900 , fontSize: 18 ) ) ) ,
2026-03-20 14:00:00 +01:00
const SizedBox ( height: 8 ) ,
2026-03-20 22:00:01 +01:00
Text ( isPublicRoom ? " Aspettiamo che uno sfidante si unisca dalla lobby pubblica. " : " Attendi che il tuo amico accetti la sfida. Non chiudere questa finestra. " , textAlign: TextAlign . center , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? theme . text : theme . text . withOpacity ( 0.8 ) , fontSize: 14 , height: 1.5 ) ) ) ,
2026-03-20 14:00:00 +01:00
] ,
) ,
) ,
) ,
] ,
) ;
if ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) {
dialogContent = AnimatedCyberBorder ( child: dialogContent ) ;
} else {
dialogContent = Container (
padding: const EdgeInsets . all ( 20 ) ,
decoration: BoxDecoration (
color: themeType = = AppThemeType . doodle ? Colors . white . withOpacity ( 0.95 ) : theme . background ,
borderRadius: BorderRadius . circular ( 25 ) ,
border: Border . all ( color: themeType = = AppThemeType . doodle ? theme . text : theme . gridLine . withOpacity ( 0.5 ) , width: 2 ) ,
boxShadow: themeType = = AppThemeType . doodle ? [ BoxShadow ( color: theme . text . withOpacity ( 0.6 ) , offset: const Offset ( 8 , 8 ) ) ] : [ ]
) ,
child: dialogContent
) ;
}
return StreamBuilder < DocumentSnapshot > (
stream: multiplayerService . listenToRoom ( code ) ,
builder: ( ctx , snapshot ) {
if ( snapshot . hasData & & snapshot . data ! . exists ) {
var data = snapshot . data ! . data ( ) as Map < String , dynamic > ;
if ( data [ ' status ' ] = = ' playing ' ) {
onRoomStarted ( ) ;
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
Navigator . pop ( ctx ) ;
2026-03-20 22:00:01 +01:00
context . read < GameController > ( ) . startNewGame ( selectedRadius , isOnline: true , roomCode: code , isHost: true , shape: selectedShape , timeMode: selectedTimeMode ) ;
Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const GameScreen ( ) ) ) ;
2026-03-20 14:00:00 +01:00
} ) ;
}
}
return PopScope (
canPop: false ,
onPopInvoked: ( didPop ) {
if ( didPop ) return ;
onCleanup ( ) ;
Navigator . pop ( ctx ) ;
} ,
child: Dialog (
backgroundColor: Colors . transparent ,
insetPadding: const EdgeInsets . all ( 20 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
dialogContent ,
const SizedBox ( height: 20 ) ,
TextButton (
onPressed: ( ) {
onCleanup ( ) ;
Navigator . pop ( ctx ) ;
} ,
child: Text ( " ANNULLA " , style: getSharedTextStyle ( themeType , TextStyle ( color: Colors . red , fontWeight: FontWeight . w900 , fontSize: 20 , letterSpacing: 2.0 , shadows: themeType = = AppThemeType . doodle ? [ ] : [ const Shadow ( color: Colors . black , blurRadius: 2 ) ] ) ) ) ,
) ,
] ,
) ,
) ,
) ;
} ,
) ;
}
) ;
}
static void showJoinPromptDialog ( BuildContext context , String roomCode , Function ( String ) onConfirm ) {
showDialog (
context: context ,
builder: ( context ) {
final themeManager = context . watch < ThemeManager > ( ) ;
final theme = themeManager . currentColors ;
final themeType = themeManager . currentThemeType ;
return AlertDialog (
backgroundColor: themeType = = AppThemeType . doodle ? Colors . white : theme . background ,
shape: themeType = = AppThemeType . doodle ? RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) , side: BorderSide ( color: theme . text , width: 2 ) ) : null ,
title: Text ( " Invito Trovato! " , style: getSharedTextStyle ( themeType , TextStyle ( color: theme . text , fontWeight: FontWeight . bold ) ) ) ,
content: Text ( " Vuoi unirti alla stanza $ roomCode ? " , style: getSharedTextStyle ( themeType , TextStyle ( color: theme . text ) ) ) ,
actions: [
TextButton ( onPressed: ( ) = > Navigator . pop ( context ) , child: Text ( " No " , style: getSharedTextStyle ( themeType , const TextStyle ( color: Colors . red ) ) ) ) ,
ElevatedButton (
style: ElevatedButton . styleFrom ( backgroundColor: themeType = = AppThemeType . doodle ? Colors . transparent : theme . playerBlue , elevation: 0 , side: themeType = = AppThemeType . doodle ? BorderSide ( color: theme . text , width: 1.5 ) : BorderSide . none ) ,
onPressed: ( ) {
Navigator . of ( context ) . pop ( ) ;
onConfirm ( roomCode ) ;
} ,
child: Text ( AppLocalizations . of ( context ) ! . joinMatch , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? theme . text : Colors . white , fontWeight: FontWeight . bold ) ) ) ,
) ,
] ,
) ;
}
) ;
}
static void showFavoritesDialog ( BuildContext context , Function ( String , String ) onInvite ) {
final favs = StorageService . instance . favorites ;
showDialog (
context: context ,
builder: ( ctx ) {
final themeManager = ctx . watch < ThemeManager > ( ) ;
final theme = themeManager . currentColors ;
final themeType = themeManager . currentThemeType ;
return AlertDialog (
backgroundColor: theme . background ,
shape: RoundedRectangleBorder (
borderRadius: BorderRadius . circular ( 20 ) ,
) ,
title: Text ( " I TUOI PREFERITI " , style: getLobbyTextStyle ( themeType , TextStyle ( color: theme . text , fontWeight: FontWeight . bold ) ) ) ,
content: Container (
width: double . maxFinite ,
height: 300 ,
decoration: BoxDecoration (
border: Border . all ( color: theme . playerRed , width: 2 ) ,
borderRadius: BorderRadius . circular ( 10 )
) ,
child: favs . isEmpty
? Center ( child: Padding (
padding: const EdgeInsets . all ( 20.0 ) ,
child: Text ( " Non hai ancora aggiunto nessun preferito dalla Classifica! " , textAlign: TextAlign . center , style: getLobbyTextStyle ( themeType , TextStyle ( color: theme . text . withOpacity ( 0.6 ) ) ) ) ,
) )
: ListView . builder (
itemCount: favs . length ,
itemBuilder: ( c , i ) {
return ListTile (
title: Text ( favs [ i ] [ ' name ' ] ! , style: getLobbyTextStyle ( themeType , TextStyle ( color: theme . text , fontSize: 18 , fontWeight: FontWeight . bold ) ) ) ,
trailing: ElevatedButton (
style: ElevatedButton . styleFrom ( backgroundColor: theme . playerBlue , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 10 ) ) ) ,
onPressed: ( ) {
Navigator . pop ( ctx ) ;
onInvite ( favs [ i ] [ ' uid ' ] ! , favs [ i ] [ ' name ' ] ! ) ;
} ,
child: Text ( " SFIDA " , style: getLobbyTextStyle ( themeType , const TextStyle ( color: Colors . white , fontWeight: FontWeight . bold ) ) ) ,
) ,
) ;
} ,
) ,
) ,
actions: [
2026-03-20 19:00:01 +01:00
TextButton (
onPressed: ( ) = > Navigator . pop ( ctx ) ,
child: Text ( " CHIUDI " , style: getLobbyTextStyle ( themeType , TextStyle ( color: theme . playerRed , fontWeight: FontWeight . bold ) ) ) ,
) ,
2026-03-20 14:00:00 +01:00
] ,
) ;
}
) ;
}
}