2026-02-27 23:35:54 +01:00
// ===========================================================================
// FILE: lib/ui/home/home_screen.dart
// ===========================================================================
import ' dart:ui ' ;
import ' package:flutter/material.dart ' ;
import ' package:provider/provider.dart ' ;
import ' package:flutter/services.dart ' ;
2026-03-01 20:59:06 +01:00
import ' package:flutter/foundation.dart ' ;
2026-03-14 00:00:01 +01:00
import ' package:font_awesome_flutter/font_awesome_flutter.dart ' ;
import ' package:cloud_firestore/cloud_firestore.dart ' ; // Serve ancora se vuoi fare logica qui, ma per ora teniamo l'import
import ' dart:async ' ;
import ' package:app_links/app_links.dart ' ;
2026-03-01 20:59:06 +01:00
import ' ../../logic/game_controller.dart ' ;
2026-02-27 23:35:54 +01:00
import ' ../../core/theme_manager.dart ' ;
import ' ../../core/app_colors.dart ' ;
import ' ../game/game_screen.dart ' ;
import ' ../settings/settings_screen.dart ' ;
import ' ../../services/storage_service.dart ' ;
import ' ../multiplayer/lobby_screen.dart ' ;
import ' history_screen.dart ' ;
2026-03-12 15:00:00 +01:00
import ' ../admin/admin_screen.dart ' ;
2026-03-14 00:00:01 +01:00
import ' package:tetraq/l10n/app_localizations.dart ' ;
2026-02-27 23:35:54 +01:00
2026-03-14 00:00:01 +01:00
// --- IMPORTIAMO I NOSTRI NUOVI WIDGET E DIALOGHI PULITI ---
import ' ../../widgets/painters.dart ' ;
import ' ../../widgets/cyber_border.dart ' ;
import ' ../../widgets/home_buttons.dart ' ;
import ' ../../widgets/music_theme_widgets.dart ' ;
import ' dialog.dart ' ; // Il file che raggruppa Quests, Leaderboard e Tutorial
2026-02-27 23:35:54 +01:00
2026-03-14 00:00:01 +01:00
// ===========================================================================
// WIDGET LOCALI PER IL SETUP DELLA PARTITA (Ancora qui per comodità)
// ===========================================================================
2026-02-27 23:35:54 +01:00
class _NeonShapeButton extends StatelessWidget {
2026-03-14 00:00:01 +01:00
final IconData icon ; final String label ; final bool isSelected ;
final ThemeColors theme ; final AppThemeType themeType ; final VoidCallback onTap ;
final bool isLocked ; final bool isSpecial ;
const _NeonShapeButton ( { required this . icon , required this . label , required this . isSelected , required this . theme , required this . themeType , required this . onTap , this . isLocked = false , this . isSpecial = false } ) ;
2026-02-27 23:35:54 +01:00
Color _getDoodleColor ( ) {
switch ( label ) {
case ' Rombo ' : return Colors . lightBlue . shade200 ;
case ' Croce ' : return Colors . green . shade200 ;
case ' Buco ' : return Colors . pink . shade200 ;
case ' Clessidra ' : return Colors . purple . shade200 ;
case ' Caos ' : return Colors . grey . shade300 ;
default : return Colors . lightBlue . shade200 ;
}
}
@ override
Widget build ( BuildContext context ) {
if ( themeType = = AppThemeType . doodle ) {
Color doodleColor = isLocked ? Colors . grey : _getDoodleColor ( ) ;
Color inkColor = const Color ( 0xFF111122 ) ;
double tilt = ( label . length % 2 = = 0 ) ? - 0.05 : 0.04 ;
return Transform . rotate (
angle: tilt ,
child: GestureDetector (
onTap: isLocked ? null : onTap ,
child: CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: isSelected ? doodleColor : Colors . white . withOpacity ( 0.8 ) , strokeColor: inkColor , seed: label . length * 3 ) ,
2026-02-27 23:35:54 +01:00
child: Container (
padding: const EdgeInsets . symmetric ( horizontal: 10 , vertical: 10 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Icon ( isLocked ? Icons . lock : icon , color: inkColor , size: 24 ) ,
const SizedBox ( height: 2 ) ,
2026-03-14 00:00:01 +01:00
Text ( isLocked ? " Liv. 10 " : label , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 11 , fontWeight: FontWeight . w900 , letterSpacing: 0.5 ) ) ) ,
2026-02-27 23:35:54 +01:00
] ,
) ,
) ,
) ,
) ,
) ;
}
Color mainColor = isSpecial & & ! isLocked ? Colors . purpleAccent : theme . playerBlue ;
return GestureDetector (
onTap: isLocked ? null : onTap ,
child: AnimatedContainer (
2026-03-14 00:00:01 +01:00
duration: const Duration ( milliseconds: 250 ) , curve: Curves . easeOutCubic , padding: const EdgeInsets . symmetric ( horizontal: 14 , vertical: 12 ) , transform: Matrix4 . translationValues ( 0 , isSelected ? 2 : 0 , 0 ) ,
2026-02-27 23:35:54 +01:00
decoration: BoxDecoration (
2026-03-14 00:00:01 +01:00
borderRadius: BorderRadius . circular ( 15 ) , border: Border . all ( color: isLocked ? Colors . transparent : ( isSelected ? mainColor : Colors . white . withOpacity ( 0.1 ) ) , width: isSelected ? 2 : 1 ) ,
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: isLocked ? [ Colors . grey . withOpacity ( 0.1 ) , Colors . black . withOpacity ( 0.2 ) ] : isSelected ? [ mainColor . withOpacity ( 0.3 ) , mainColor . withOpacity ( 0.1 ) ] : [ theme . text . withOpacity ( 0.1 ) , theme . text . withOpacity ( 0.02 ) ] ) ,
boxShadow: isLocked ? [ ] : isSelected ? [ BoxShadow ( color: mainColor . withOpacity ( 0.5 ) , blurRadius: 15 , spreadRadius: 1 , offset: const Offset ( 0 , 0 ) ) ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.4 ) , blurRadius: 6 , offset: const Offset ( 2 , 4 ) ) , BoxShadow ( color: Colors . white . withOpacity ( 0.05 ) , blurRadius: 2 , offset: const Offset ( - 1 , - 1 ) ) ] ,
2026-02-27 23:35:54 +01:00
) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Icon ( isLocked ? Icons . lock : icon , color: isLocked ? Colors . grey . withOpacity ( 0.5 ) : ( isSelected ? Colors . white : theme . text . withOpacity ( 0.6 ) ) , size: 24 ) ,
const SizedBox ( height: 6 ) ,
2026-03-14 00:00:01 +01:00
Text ( isLocked ? " Liv. 10 " : label , style: getSharedTextStyle ( themeType , TextStyle ( color: isLocked ? Colors . grey . withOpacity ( 0.5 ) : ( isSelected ? Colors . white : theme . text . withOpacity ( 0.6 ) ) , fontSize: 11 , fontWeight: isSelected ? FontWeight . w900 : FontWeight . bold ) ) ) ,
2026-02-27 23:35:54 +01:00
] ,
) ,
) ,
) ;
}
}
class _NeonSizeButton extends StatelessWidget {
2026-03-14 00:00:01 +01:00
final String label ; final bool isSelected ; final ThemeColors theme ; final AppThemeType themeType ; final VoidCallback onTap ;
2026-02-27 23:35:54 +01:00
const _NeonSizeButton ( { required this . label , required this . isSelected , required this . theme , required this . themeType , required this . onTap } ) ;
@ override
Widget build ( BuildContext context ) {
if ( themeType = = AppThemeType . doodle ) {
2026-03-14 00:00:01 +01:00
Color doodleColor = label = = ' MAX ' ? Colors . red . shade200 : Colors . cyan . shade100 ; Color inkColor = const Color ( 0xFF111122 ) ; double tilt = ( label = = ' M ' | | label = = ' MAX ' ) ? 0.05 : - 0.04 ;
2026-02-27 23:35:54 +01:00
return Transform . rotate (
angle: tilt ,
child: GestureDetector (
onTap: onTap ,
2026-03-14 00:00:01 +01:00
child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: isSelected ? doodleColor : Colors . white . withOpacity ( 0.8 ) , strokeColor: inkColor , seed: label . codeUnitAt ( 0 ) , isCircle: true ) , child: SizedBox ( width: 50 , height: 50 , child: Center ( child: Text ( label , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 18 , fontWeight: FontWeight . w900 ) ) ) ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
) ;
}
return GestureDetector (
onTap: onTap ,
child: AnimatedContainer (
2026-03-14 00:00:01 +01:00
duration: const Duration ( milliseconds: 250 ) , curve: Curves . easeOutCubic , width: 50 , height: 50 , transform: Matrix4 . translationValues ( 0 , isSelected ? 2 : 0 , 0 ) ,
2026-02-27 23:35:54 +01:00
decoration: BoxDecoration (
2026-03-14 00:00:01 +01:00
shape: BoxShape . circle , border: Border . all ( color: isSelected ? theme . playerRed : Colors . white . withOpacity ( 0.1 ) , width: isSelected ? 2 : 1 ) ,
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: isSelected ? [ theme . playerRed . withOpacity ( 0.3 ) , theme . playerRed . withOpacity ( 0.1 ) ] : [ theme . text . withOpacity ( 0.1 ) , theme . text . withOpacity ( 0.02 ) ] ) ,
boxShadow: isSelected ? [ BoxShadow ( color: theme . playerRed . withOpacity ( 0.5 ) , blurRadius: 15 , spreadRadius: 1 ) ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.4 ) , blurRadius: 6 , offset: const Offset ( 2 , 4 ) ) , BoxShadow ( color: Colors . white . withOpacity ( 0.05 ) , blurRadius: 2 , offset: const Offset ( - 1 , - 1 ) ) ] ,
2026-02-27 23:35:54 +01:00
) ,
2026-03-14 00:00:01 +01:00
child: Center ( child: Text ( label , style: getSharedTextStyle ( themeType , TextStyle ( color: isSelected ? Colors . white : theme . text . withOpacity ( 0.6 ) , fontSize: 14 , fontWeight: isSelected ? FontWeight . w900 : FontWeight . bold ) ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
) ;
}
}
class _NeonTimeSwitch extends StatelessWidget {
2026-03-14 00:00:01 +01:00
final bool isTimeMode ; final ThemeColors theme ; final AppThemeType themeType ; final VoidCallback onTap ;
2026-02-27 23:35:54 +01:00
const _NeonTimeSwitch ( { required this . isTimeMode , required this . theme , required this . themeType , required this . onTap } ) ;
@ override
Widget build ( BuildContext context ) {
if ( themeType = = AppThemeType . doodle ) {
2026-03-14 00:00:01 +01:00
Color doodleColor = Colors . orange . shade200 ; Color inkColor = const Color ( 0xFF111122 ) ;
2026-02-27 23:35:54 +01:00
return Transform . rotate (
angle: - 0.015 ,
child: GestureDetector (
onTap: onTap ,
child: CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: isTimeMode ? doodleColor : Colors . white . withOpacity ( 0.8 ) , strokeColor: inkColor , seed: 42 ) ,
2026-02-27 23:35:54 +01:00
child: Container (
padding: const EdgeInsets . symmetric ( horizontal: 20 , vertical: 12 ) ,
child: Row (
2026-03-14 00:00:01 +01:00
mainAxisSize: MainAxisSize . min , mainAxisAlignment: MainAxisAlignment . center ,
2026-02-27 23:35:54 +01:00
children: [
2026-03-14 00:00:01 +01:00
Icon ( isTimeMode ? Icons . timer : Icons . timer_off , color: inkColor , size: 28 ) , const SizedBox ( width: 12 ) ,
Column ( crossAxisAlignment: CrossAxisAlignment . start , mainAxisSize: MainAxisSize . min , children: [ Text ( isTimeMode ? ' A TEMPO ' : ' RELAX ' , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontWeight: FontWeight . w900 , fontSize: 16 , letterSpacing: 2.0 ) ) ) , Text ( isTimeMode ? ' 15 sec a mossa ' : ' Nessun limite ' , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor . withOpacity ( 0.8 ) , fontSize: 13 , fontWeight: FontWeight . bold ) ) ) ] ) ,
2026-02-27 23:35:54 +01:00
] ,
) ,
) ,
) ,
) ,
) ;
}
return GestureDetector (
onTap: onTap ,
child: AnimatedContainer (
2026-03-14 00:00:01 +01:00
duration: const Duration ( milliseconds: 300 ) , curve: Curves . easeInOut , padding: const EdgeInsets . symmetric ( horizontal: 16 , vertical: 12 ) ,
2026-02-27 23:35:54 +01:00
decoration: BoxDecoration (
2026-03-14 00:00:01 +01:00
borderRadius: BorderRadius . circular ( 20 ) , border: Border . all ( color: isTimeMode ? Colors . amber : Colors . white . withOpacity ( 0.1 ) , width: isTimeMode ? 2 : 1 ) ,
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: isTimeMode ? [ Colors . amber . withOpacity ( 0.25 ) , Colors . amber . withOpacity ( 0.05 ) ] : [ theme . text . withOpacity ( 0.1 ) , theme . text . withOpacity ( 0.02 ) ] ) ,
boxShadow: isTimeMode ? [ BoxShadow ( color: Colors . amber . withOpacity ( 0.3 ) , blurRadius: 15 , spreadRadius: 2 ) ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.4 ) , blurRadius: 6 , offset: const Offset ( 2 , 4 ) ) , BoxShadow ( color: Colors . white . withOpacity ( 0.05 ) , blurRadius: 2 , offset: const Offset ( - 1 , - 1 ) ) ] ,
2026-02-27 23:35:54 +01:00
) ,
child: Row (
2026-03-14 00:00:01 +01:00
mainAxisSize: MainAxisSize . max , mainAxisAlignment: MainAxisAlignment . center ,
2026-02-27 23:35:54 +01:00
children: [
2026-03-14 00:00:01 +01:00
Icon ( isTimeMode ? Icons . timer : Icons . timer_off , color: isTimeMode ? Colors . amber : theme . text . withOpacity ( 0.5 ) , size: 28 ) , const SizedBox ( width: 12 ) ,
Column ( crossAxisAlignment: CrossAxisAlignment . start , mainAxisSize: MainAxisSize . min , children: [ Text ( isTimeMode ? ' A TEMPO ' : ' RELAX ' , style: getSharedTextStyle ( themeType , TextStyle ( color: isTimeMode ? Colors . white : theme . text . withOpacity ( 0.5 ) , fontWeight: FontWeight . w900 , fontSize: 14 , letterSpacing: 1.5 ) ) ) , Text ( isTimeMode ? ' 15 sec a mossa ' : ' Nessun limite ' , style: getSharedTextStyle ( themeType , TextStyle ( color: isTimeMode ? Colors . amber . shade200 : theme . text . withOpacity ( 0.4 ) , fontSize: 11 , fontWeight: FontWeight . bold ) ) ) ] ) ,
2026-02-27 23:35:54 +01:00
] ,
) ,
) ,
) ;
}
}
2026-03-14 00:00:01 +01:00
// ===========================================================================
// CLASSE PRINCIPALE HOME
// ===========================================================================
2026-02-27 23:35:54 +01:00
class HomeScreen extends StatefulWidget {
const HomeScreen ( { super . key } ) ;
@ override
State < HomeScreen > createState ( ) = > _HomeScreenState ( ) ;
}
class _HomeScreenState extends State < HomeScreen > with WidgetsBindingObserver {
2026-03-01 20:59:06 +01:00
int _debugTapCount = 0 ;
2026-03-12 21:00:08 +01:00
late AppLinks _appLinks ;
StreamSubscription < Uri > ? _linkSubscription ;
2026-02-27 23:35:54 +01:00
@ override
void initState ( ) {
super . initState ( ) ;
WidgetsBinding . instance . addObserver ( this ) ;
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
_checkPlayerName ( ) ;
2026-03-12 21:00:08 +01:00
StorageService . instance . syncLeaderboard ( ) ;
2026-02-27 23:35:54 +01:00
} ) ;
_checkClipboardForInvite ( ) ;
2026-03-13 22:00:00 +01:00
_initDeepLinks ( ) ;
2026-02-27 23:35:54 +01:00
}
@ override
void dispose ( ) {
WidgetsBinding . instance . removeObserver ( this ) ;
2026-03-13 22:00:00 +01:00
_linkSubscription ? . cancel ( ) ;
2026-02-27 23:35:54 +01:00
super . dispose ( ) ;
}
@ override
void didChangeAppLifecycleState ( AppLifecycleState state ) {
if ( state = = AppLifecycleState . resumed ) {
_checkClipboardForInvite ( ) ;
}
}
void _checkPlayerName ( ) {
2026-03-14 00:00:01 +01:00
if ( StorageService . instance . playerName . isEmpty ) { _showNameDialog ( ) ; }
}
Future < void > _initDeepLinks ( ) async {
_appLinks = AppLinks ( ) ;
try {
final initialUri = await _appLinks . getInitialLink ( ) ;
if ( initialUri ! = null ) _handleDeepLink ( initialUri ) ;
} catch ( e ) { debugPrint ( " Errore lettura link iniziale: $ e " ) ; }
_linkSubscription = _appLinks . uriLinkStream . listen ( ( uri ) { _handleDeepLink ( uri ) ; } , onError: ( err ) { debugPrint ( " Errore stream link: $ err " ) ; } ) ;
}
void _handleDeepLink ( Uri uri ) {
if ( uri . scheme = = ' tetraq ' & & uri . host = = ' join ' ) {
String ? code = uri . queryParameters [ ' code ' ] ;
if ( code ! = null & & code . length = = 5 ) {
Future . delayed ( const Duration ( milliseconds: 500 ) , ( ) {
if ( mounted ) _promptJoinRoom ( code . toUpperCase ( ) ) ;
} ) ;
}
2026-02-27 23:35:54 +01:00
}
}
2026-03-14 00:00:01 +01:00
Future < void > _checkClipboardForInvite ( ) async {
try {
ClipboardData ? data = await Clipboard . getData ( Clipboard . kTextPlain ) ;
String ? text = data ? . text ;
if ( text ! = null & & text . contains ( " TetraQ " ) & & text . contains ( " codice: " ) ) {
RegExp regExp = RegExp ( r'codice:\s*([A-Z0-9]{5})' , caseSensitive: false ) ;
Match ? match = regExp . firstMatch ( text ) ;
if ( match ! = null ) {
String roomCode = match . group ( 1 ) ! . toUpperCase ( ) ;
await Clipboard . setData ( const ClipboardData ( text: ' ' ) ) ;
if ( mounted & & ModalRoute . of ( context ) ? . isCurrent = = true ) { _promptJoinRoom ( roomCode ) ; }
}
}
} catch ( e ) { debugPrint ( " Errore lettura appunti: $ e " ) ; }
}
// ===========================================================================
// DIALOGHI IN-FILE (Setup e Nome)
// ===========================================================================
void _promptJoinRoom ( String roomCode ) {
showDialog (
context: context ,
builder: ( context ) {
final theme = context . watch < ThemeManager > ( ) . currentColors ;
final themeType = context . read < 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 ( ) ;
Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > LobbyScreen ( initialRoomCode: roomCode ) ) ) ;
} ,
child: Text ( AppLocalizations . of ( context ) ! . joinMatch , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? theme . text : Colors . white , fontWeight: FontWeight . bold ) ) ) ,
) ,
] ,
) ;
}
) ;
}
2026-02-27 23:35:54 +01:00
void _showNameDialog ( ) {
final TextEditingController nameController = TextEditingController ( text: StorageService . instance . playerName ) ;
showDialog (
2026-03-14 00:00:01 +01:00
context: context , barrierDismissible: false , barrierColor: Colors . black . withOpacity ( 0.8 ) ,
2026-02-27 23:35:54 +01:00
builder: ( context ) {
final themeManager = context . watch < ThemeManager > ( ) ;
2026-03-14 00:00:01 +01:00
final theme = themeManager . currentColors ; final themeType = themeManager . currentThemeType ;
Color inkColor = const Color ( 0xFF111122 ) ; final loc = AppLocalizations . of ( context ) ! ;
2026-02-27 23:35:54 +01:00
Widget dialogContent = themeType = = AppThemeType . doodle
? CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: Colors . yellow . shade100 , strokeColor: inkColor , seed: 100 ) ,
2026-02-27 23:35:54 +01:00
child: Padding (
padding: const EdgeInsets . symmetric ( vertical: 40.0 , horizontal: 25.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
2026-03-14 00:00:01 +01:00
Text ( loc . welcomeTitle , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontWeight: FontWeight . w900 , fontSize: 28 , letterSpacing: 2.0 ) ) , textAlign: TextAlign . center ) ,
2026-02-27 23:35:54 +01:00
const SizedBox ( height: 20 ) ,
2026-03-14 00:00:01 +01:00
Text ( ' Scegli il tuo nome da battaglia ' , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor . withOpacity ( 0.8 ) , fontSize: 18 ) ) , textAlign: TextAlign . center ) ,
2026-02-27 23:35:54 +01:00
const SizedBox ( height: 30 ) ,
TextField (
controller: nameController , textCapitalization: TextCapitalization . characters , textAlign: TextAlign . center , maxLength: 5 ,
2026-03-14 00:00:01 +01:00
style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 36 , fontWeight: FontWeight . bold , letterSpacing: 8 ) ) ,
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 ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
const SizedBox ( height: 40 ) ,
GestureDetector (
onTap: ( ) {
final name = nameController . text . trim ( ) ;
2026-03-14 00:00:01 +01:00
if ( name . isNotEmpty ) { StorageService . instance . savePlayerName ( name ) ; Navigator . of ( context ) . pop ( ) ; setState ( ( ) { } ) ; }
2026-02-27 23:35:54 +01:00
} ,
child: CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: Colors . green . shade200 , strokeColor: inkColor , seed: 101 ) ,
child: Container ( width: double . infinity , height: 60 , alignment: Alignment . center , child: Text ( loc . saveAndPlay , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontSize: 22 , fontWeight: FontWeight . bold , letterSpacing: 1.5 ) ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
) ,
] ,
) ,
) ,
)
: Container (
2026-03-14 00:00:01 +01:00
decoration: BoxDecoration ( color: theme . background , borderRadius: BorderRadius . circular ( 25 ) , border: Border . all ( color: theme . playerBlue . withOpacity ( 0.5 ) , width: 2 ) , boxShadow: [ BoxShadow ( color: theme . playerBlue . withOpacity ( 0.3 ) , blurRadius: 20 , spreadRadius: 5 ) ] ) ,
2026-02-27 23:35:54 +01:00
child: SingleChildScrollView (
child: Padding (
padding: const EdgeInsets . symmetric ( vertical: 30.0 , horizontal: 25.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
2026-03-14 00:00:01 +01:00
Text ( loc . welcomeTitle , style: getSharedTextStyle ( themeType , TextStyle ( color: theme . text , fontWeight: FontWeight . w900 , fontSize: 24 , letterSpacing: 1.5 ) ) , textAlign: TextAlign . center ) ,
2026-02-27 23:35:54 +01:00
const SizedBox ( height: 20 ) ,
2026-03-14 00:00:01 +01:00
Text ( ' Scegli il tuo nome da battaglia per sfidare i tuoi amici online. ' , style: getSharedTextStyle ( themeType , TextStyle ( color: theme . text . withOpacity ( 0.8 ) , fontSize: 16 ) ) , textAlign: TextAlign . center ) ,
2026-02-27 23:35:54 +01:00
const SizedBox ( height: 40 ) ,
TextField (
controller: nameController , textCapitalization: TextCapitalization . characters , textAlign: TextAlign . center , maxLength: 5 ,
2026-03-14 00:00:01 +01:00
style: getSharedTextStyle ( themeType , TextStyle ( color: theme . text , fontSize: 32 , fontWeight: FontWeight . bold , letterSpacing: 8 ) ) ,
decoration: InputDecoration ( hintText: loc . nameHint , hintStyle: getSharedTextStyle ( themeType , TextStyle ( color: theme . text . withOpacity ( 0.3 ) , letterSpacing: 4 ) ) , filled: true , fillColor: theme . text . withOpacity ( 0.05 ) , counterText: " " , enabledBorder: OutlineInputBorder ( borderSide: BorderSide ( color: theme . gridLine . withOpacity ( 0.5 ) , width: 2 ) , borderRadius: BorderRadius . circular ( 15 ) ) , focusedBorder: OutlineInputBorder ( borderSide: BorderSide ( color: theme . playerBlue , width: 3 ) , borderRadius: BorderRadius . circular ( 15 ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
const SizedBox ( height: 40 ) ,
SizedBox (
width: double . infinity , height: 55 ,
child: ElevatedButton (
style: ElevatedButton . styleFrom ( backgroundColor: theme . playerBlue , foregroundColor: Colors . white , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) ) ,
onPressed: ( ) {
final name = nameController . text . trim ( ) ;
2026-03-14 00:00:01 +01:00
if ( name . isNotEmpty ) { StorageService . instance . savePlayerName ( name ) ; Navigator . of ( context ) . pop ( ) ; setState ( ( ) { } ) ; }
2026-02-27 23:35:54 +01:00
} ,
2026-03-14 00:00:01 +01:00
child: Text ( loc . saveAndPlay , style: getSharedTextStyle ( themeType , const TextStyle ( fontSize: 18 , fontWeight: FontWeight . bold , letterSpacing: 1.5 ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
) ,
] ,
) ,
) ,
) ,
) ;
2026-03-14 00:00:01 +01:00
if ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) dialogContent = AnimatedCyberBorder ( child: dialogContent ) ;
2026-02-27 23:35:54 +01:00
return Dialog ( backgroundColor: Colors . transparent , insetPadding: const EdgeInsets . all ( 20 ) , child: dialogContent ) ;
} ,
) ;
}
void _showMatchSetupDialog ( bool isVsCPU ) {
2026-03-14 00:00:01 +01:00
int localRadius = 4 ; ArenaShape localShape = ArenaShape . classic ; bool localTimeMode = true ;
2026-03-01 20:59:06 +01:00
bool isChaosUnlocked = StorageService . instance . playerLevel > = 10 ;
2026-03-05 15:00:00 +01:00
final loc = AppLocalizations . of ( context ) ! ;
2026-02-27 23:35:54 +01:00
showDialog (
2026-03-14 00:00:01 +01:00
context: context , barrierColor: Colors . black . withOpacity ( 0.8 ) ,
2026-02-27 23:35:54 +01:00
builder: ( ctx ) {
final themeManager = ctx . watch < ThemeManager > ( ) ;
2026-03-14 00:00:01 +01:00
final theme = themeManager . currentColors ; final themeType = themeManager . currentThemeType ;
2026-02-27 23:35:54 +01:00
Color inkColor = const Color ( 0xFF111122 ) ;
return StatefulBuilder (
builder: ( context , setStateDialog ) {
Widget dialogContent = themeType = = AppThemeType . doodle
? Transform . rotate (
angle: 0.015 ,
child: CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: Colors . white . withOpacity ( 0.95 ) , strokeColor: inkColor , seed: 200 ) ,
2026-02-27 23:35:54 +01:00
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . all ( 25.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
2026-03-14 00:00:01 +01:00
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 ) ] ) ,
2026-02-27 23:35:54 +01:00
const SizedBox ( height: 25 ) ,
2026-03-13 22:00:00 +01:00
if ( isVsCPU ) . . . [
2026-03-14 00:00:01 +01:00
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 ) ,
Text ( " Livello CPU: ${ StorageService . instance . cpuLevel } \n Forma e dimensioni 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 ) ,
Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
2026-03-13 22:00:00 +01:00
] else . . . [
2026-03-14 00:00:01 +01:00
Text ( " FORMA ARENA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 15 ) ,
2026-03-13 22:00:00 +01:00
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 ) ) ,
] ,
) ,
2026-03-14 00:00:01 +01:00
const SizedBox ( height: 25 ) , Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
2026-02-27 23:35:54 +01:00
2026-03-14 00:00:01 +01:00
Text ( " GRANDEZZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 15 ) ,
2026-03-13 22:00:00 +01:00
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 ) ) ,
] ,
) ,
2026-03-14 00:00:01 +01:00
const SizedBox ( height: 25 ) , Divider ( color: inkColor . withOpacity ( 0.3 ) , thickness: 2.5 ) , const SizedBox ( height: 20 ) ,
2026-03-13 22:00:00 +01:00
] ,
2026-02-27 23:35:54 +01:00
2026-03-14 00:00:01 +01:00
Text ( " TEMPO " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 14 , fontWeight: FontWeight . w900 , color: inkColor . withOpacity ( 0.6 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
_NeonTimeSwitch ( isTimeMode: localTimeMode , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localTimeMode = ! localTimeMode ) ) , const SizedBox ( height: 35 ) ,
2026-02-27 23:35:54 +01:00
Transform . rotate (
angle: - 0.02 ,
child: GestureDetector (
2026-03-14 00: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 ( ) ) ) ; } ,
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 ) ) ) ) ) ,
2026-02-27 23:35:54 +01:00
) ,
)
] ,
) ,
) ,
) ,
) ,
)
: Container (
decoration: BoxDecoration (
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: [ theme . background . withOpacity ( 0.95 ) , theme . background . withOpacity ( 0.8 ) ] ) ,
2026-03-14 00:00:01 +01:00
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 ) ,
2026-03-13 23:00:01 +01:00
boxShadow: themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . arcade | | themeType = = AppThemeType . music ? [ ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.5 ) , blurRadius: 20 , offset: const Offset ( 4 , 10 ) ) ] ,
2026-02-27 23:35:54 +01:00
) ,
child: SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: Padding (
padding: const EdgeInsets . all ( 20.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
2026-03-14 00:00:01 +01:00
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 ) ] ) ,
2026-02-27 23:35:54 +01:00
const SizedBox ( height: 20 ) ,
2026-03-13 22:00:00 +01:00
if ( isVsCPU ) . . . [
2026-03-14 00:00:01 +01:00
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 ) ,
Text ( " Livello CPU: ${ StorageService . instance . cpuLevel } \n Forma e dimensioni 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 ) ,
Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
2026-03-13 22:00:00 +01:00
] else . . . [
2026-03-14 00:00:01 +01:00
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 ) ,
2026-03-13 22:00:00 +01:00
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 ) ) ,
] ,
) ,
2026-03-14 00:00:01 +01:00
const SizedBox ( height: 20 ) , Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
2026-02-27 23:35:54 +01:00
2026-03-14 00:00:01 +01:00
Text ( " GRANDEZZA " , style: getSharedTextStyle ( themeType , TextStyle ( fontSize: 12 , fontWeight: FontWeight . w900 , color: theme . text . withOpacity ( 0.5 ) , letterSpacing: 1.5 ) ) ) , const SizedBox ( height: 10 ) ,
2026-03-13 22:00:00 +01:00
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 ) ) ,
] ,
) ,
2026-03-14 00:00:01 +01:00
const SizedBox ( height: 20 ) , Divider ( color: Colors . white . withOpacity ( 0.05 ) , thickness: 2 ) , const SizedBox ( height: 20 ) ,
2026-03-13 22:00:00 +01:00
] ,
2026-02-27 23:35:54 +01:00
2026-03-14 00: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 ) ,
_NeonTimeSwitch ( isTimeMode: localTimeMode , theme: theme , themeType: themeType , onTap: ( ) = > setStateDialog ( ( ) = > localTimeMode = ! localTimeMode ) ) , const SizedBox ( height: 30 ) ,
2026-02-27 23:35:54 +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-14 00: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-05 15:00:00 +01:00
child: Text ( loc . startGame , style: const TextStyle ( fontSize: 18 , fontWeight: FontWeight . w900 , letterSpacing: 2 ) ) ,
2026-02-27 23:35:54 +01:00
) ,
)
] ,
) ,
) ,
) ,
) ;
2026-03-13 23:00:01 +01:00
if ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) {
2026-03-14 00:00:01 +01:00
dialogContent = AnimatedCyberBorder ( child: dialogContent ) ;
2026-02-27 23:35:54 +01:00
}
return Dialog ( backgroundColor: Colors . transparent , insetPadding: const EdgeInsets . symmetric ( horizontal: 15 , vertical: 20 ) , child: dialogContent ) ;
} ,
) ;
}
) ;
}
2026-03-01 20:59:06 +01:00
2026-03-14 00:00:01 +01:00
// ===========================================================================
// INTERFACCIA PRINCIPALE
// ===========================================================================
2026-02-27 23:35:54 +01:00
Widget _buildCyberCard ( Widget card , AppThemeType themeType ) {
2026-03-14 00:00:01 +01:00
if ( themeType = = AppThemeType . cyberpunk ) return AnimatedCyberBorder ( child: card ) ;
return card ;
2026-02-27 23:35:54 +01:00
}
@ override
Widget build ( BuildContext context ) {
SystemChrome . setEnabledSystemUIMode ( SystemUiMode . immersiveSticky ) ;
SystemChrome . setPreferredOrientations ( [ DeviceOrientation . portraitUp , DeviceOrientation . portraitDown ] ) ;
final themeManager = context . watch < ThemeManager > ( ) ;
final themeType = themeManager . currentThemeType ;
final theme = themeManager . currentColors ;
Color inkColor = const Color ( 0xFF111122 ) ;
2026-03-05 15:00:00 +01:00
final loc = AppLocalizations . of ( context ) ! ;
2026-02-27 23:35:54 +01:00
String ? bgImage ;
if ( themeType = = AppThemeType . wood ) bgImage = ' assets/images/wood_bg.jpg ' ;
if ( themeType = = AppThemeType . cyberpunk ) bgImage = ' assets/images/cyber_bg.jpg ' ;
2026-03-13 23:00:01 +01:00
if ( themeType = = AppThemeType . music ) bgImage = ' assets/images/music_bg.jpg ' ;
2026-02-27 23:35:54 +01:00
int wins = StorageService . instance . wins ;
int losses = StorageService . instance . losses ;
String playerName = StorageService . instance . playerName ;
if ( playerName . isEmpty ) playerName = " GUEST " ;
2026-03-01 20:59:06 +01:00
int level = StorageService . instance . playerLevel ;
int currentXP = StorageService . instance . totalXP ;
double xpProgress = ( currentXP % 100 ) / 100.0 ;
2026-02-27 23:35:54 +01:00
Widget uiContent = SafeArea (
2026-03-01 20:59:06 +01:00
child: LayoutBuilder (
builder: ( context , constraints ) {
return SingleChildScrollView (
physics: const BouncingScrollPhysics ( ) ,
child: ConstrainedBox (
2026-03-14 00:00:01 +01:00
constraints: BoxConstraints ( minHeight: constraints . maxHeight ) ,
2026-03-01 20:59:06 +01:00
child: IntrinsicHeight (
child: Padding (
padding: const EdgeInsets . symmetric ( horizontal: 20.0 , vertical: 20.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . stretch ,
2026-02-27 23:35:54 +01:00
children: [
2026-03-01 20:59:06 +01:00
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
GestureDetector (
onTap: _showNameDialog ,
child: Row (
children: [
themeType = = AppThemeType . doodle
? CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: Colors . white . withOpacity ( 0.8 ) , strokeColor: inkColor , seed: 1 , isCircle: true ) ,
2026-03-01 20:59:06 +01:00
child: SizedBox ( width: 50 , height: 50 , child: Icon ( Icons . person , color: inkColor , size: 30 ) ) ,
)
: SizedBox (
width: 50 , height: 50 ,
child: Stack (
fit: StackFit . expand ,
children: [
CircularProgressIndicator ( value: xpProgress , color: theme . playerBlue , strokeWidth: 3 , backgroundColor: theme . gridLine . withOpacity ( 0.2 ) ) ,
Padding (
padding: const EdgeInsets . all ( 4.0 ) ,
child: Container (
decoration: BoxDecoration ( shape: BoxShape . circle , boxShadow: [ BoxShadow ( color: theme . playerBlue . withOpacity ( 0.3 ) , blurRadius: 10 , offset: const Offset ( 0 , 4 ) ) ] ) ,
child: CircleAvatar ( backgroundColor: theme . playerBlue . withOpacity ( 0.2 ) , child: Icon ( Icons . person , color: theme . playerBlue , size: 26 ) ) ,
) ,
) ,
] ,
) ,
) ,
const SizedBox ( width: 12 ) ,
Column (
crossAxisAlignment: CrossAxisAlignment . start ,
mainAxisSize: MainAxisSize . min ,
children: [
2026-03-14 00:00:01 +01:00
Text ( playerName , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? inkColor : theme . text , fontSize: 24 , fontWeight: FontWeight . w900 , letterSpacing: 1.5 , shadows: themeType = = AppThemeType . doodle ? [ ] : [ Shadow ( color: Colors . black . withOpacity ( 0.5 ) , offset: const Offset ( 1 , 2 ) , blurRadius: 2 ) ] ) ) ) ,
Text ( " LIV. $ level " , style: getSharedTextStyle ( themeType , TextStyle ( color: themeType = = AppThemeType . doodle ? inkColor . withOpacity ( 0.8 ) : theme . playerBlue , fontSize: 14 , fontWeight: FontWeight . bold , letterSpacing: 1 ) ) ) ,
2026-03-01 20:59:06 +01:00
] ,
) ,
] ,
) ,
) ,
GestureDetector (
onTap: ( ) = > Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const HistoryScreen ( ) ) ) ,
child: themeType = = AppThemeType . doodle
? Transform . rotate (
angle: 0.04 ,
child: CustomPaint (
2026-03-14 00:00:01 +01:00
painter: DoodleBackgroundPainter ( fillColor: Colors . yellow . shade100 , strokeColor: inkColor , seed: 2 ) ,
2026-03-01 20:59:06 +01:00
child: Padding (
padding: const EdgeInsets . symmetric ( horizontal: 16 , vertical: 12 ) ,
child: Row (
children: [
Icon ( Icons . emoji_events , color: inkColor , size: 20 ) , const SizedBox ( width: 6 ) ,
2026-03-14 00:00:01 +01:00
Text ( " $ wins " , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontWeight: FontWeight . w900 ) ) ) , const SizedBox ( width: 12 ) ,
2026-03-01 20:59:06 +01:00
Icon ( Icons . sentiment_very_dissatisfied , color: inkColor , size: 20 ) , const SizedBox ( width: 6 ) ,
2026-03-14 00:00:01 +01:00
Text ( " $ losses " , style: getSharedTextStyle ( themeType , TextStyle ( color: inkColor , fontWeight: FontWeight . w900 ) ) ) ,
2026-03-01 20:59:06 +01:00
] ,
) ,
) ,
) ,
)
: Container (
padding: const EdgeInsets . symmetric ( horizontal: 14 , vertical: 10 ) ,
decoration: BoxDecoration (
gradient: LinearGradient ( begin: Alignment . topLeft , end: Alignment . bottomRight , colors: [ theme . text . withOpacity ( 0.15 ) , theme . text . withOpacity ( 0.02 ) ] ) ,
borderRadius: BorderRadius . circular ( 20 ) ,
border: Border . all ( color: Colors . white . withOpacity ( 0.1 ) , width: 1.5 ) ,
boxShadow: [ BoxShadow ( color: Colors . black . withOpacity ( 0.3 ) , offset: const Offset ( 2 , 4 ) , blurRadius: 8 ) , BoxShadow ( color: Colors . white . withOpacity ( 0.05 ) , offset: const Offset ( - 1 , - 1 ) , blurRadius: 2 ) ] ,
) ,
child: Row (
children: [
2026-03-13 23:00:01 +01:00
Icon ( themeType = = AppThemeType . music ? FontAwesomeIcons . microphone : Icons . emoji_events , color: Colors . amber . shade600 , size: 16 ) , const SizedBox ( width: 6 ) ,
2026-03-14 00:00:01 +01:00
Text ( " $ wins " , style: getSharedTextStyle ( themeType , const TextStyle ( color: Colors . white , fontWeight: FontWeight . w900 ) ) ) , const SizedBox ( width: 12 ) ,
2026-03-13 23:00:01 +01:00
Icon ( themeType = = AppThemeType . music ? FontAwesomeIcons . compactDisc : Icons . sentiment_very_dissatisfied , color: theme . playerRed . withOpacity ( 0.8 ) , size: 16 ) , const SizedBox ( width: 6 ) ,
2026-03-14 00:00:01 +01:00
Text ( " $ losses " , style: getSharedTextStyle ( themeType , const TextStyle ( color: Colors . white , fontWeight: FontWeight . w900 ) ) ) ,
2026-03-01 20:59:06 +01:00
] ,
) ,
) ,
)
] ,
2026-02-27 23:35:54 +01:00
) ,
2026-03-01 20:59:06 +01:00
const Spacer ( ) ,
Center (
child: Transform . rotate (
angle: themeType = = AppThemeType . doodle ? - 0.04 : 0 ,
child: GestureDetector (
onTap: ( ) {
_debugTapCount + + ;
2026-03-12 15:00:00 +01:00
if ( _debugTapCount = = 5 ) {
2026-03-01 20:59:06 +01:00
StorageService . instance . addXP ( 2000 ) ;
setState ( ( ) { } ) ;
ScaffoldMessenger . of ( context ) . showSnackBar (
2026-03-14 00:00:01 +01:00
SnackBar ( content: Text ( " 🛠 DEBUG MODE: +20 Livelli! " , style: getSharedTextStyle ( themeType , const TextStyle ( color: Colors . white , fontWeight: FontWeight . bold ) ) ) , backgroundColor: Colors . purpleAccent , behavior: SnackBarBehavior . floating , shape: RoundedRectangleBorder ( borderRadius: BorderRadius . circular ( 15 ) ) )
2026-03-01 20:59:06 +01:00
) ;
2026-03-12 15:00:00 +01:00
} else if ( _debugTapCount > = 7 ) {
2026-03-13 23:00:01 +01:00
_debugTapCount = 0 ;
2026-03-12 15:00:00 +01:00
Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const AdminScreen ( ) ) ) ;
2026-03-01 20:59:06 +01:00
}
} ,
child: FittedBox (
fit: BoxFit . scaleDown ,
child: Text (
2026-03-05 15:00:00 +01:00
loc . appTitle . toUpperCase ( ) ,
2026-03-14 00:00:01 +01:00
style: getSharedTextStyle ( themeType , TextStyle (
2026-03-01 20:59:06 +01:00
fontSize: 65 ,
fontWeight: FontWeight . w900 ,
color: themeType = = AppThemeType . doodle ? inkColor : theme . text ,
letterSpacing: 10 ,
2026-03-13 23:00:01 +01:00
shadows: themeType = = AppThemeType . doodle
2026-03-14 00:00:01 +01:00
? [ const Shadow ( color: Colors . white , offset: Offset ( - 2.5 , - 2.5 ) , blurRadius: 0 ) , Shadow ( color: Colors . black . withOpacity ( 0.25 ) , offset: const Offset ( 2.5 , 2.5 ) , blurRadius: 1 ) ]
: themeType = = AppThemeType . arcade | | themeType = = AppThemeType . music ? [ ] : [ BoxShadow ( color: Colors . black . withOpacity ( 0.6 ) , offset: const Offset ( 3 , 6 ) , blurRadius: 8 ) , BoxShadow ( color: theme . playerBlue . withOpacity ( 0.4 ) , offset: const Offset ( 0 , 0 ) , blurRadius: 20 ) ]
2026-03-01 20:59:06 +01:00
) )
) ,
) ,
) ,
2026-02-27 23:35:54 +01:00
) ,
) ,
2026-03-01 20:59:06 +01:00
const Spacer ( ) ,
2026-02-27 23:35:54 +01:00
2026-03-14 00:00:01 +01:00
// --- MENU IN BASE AL TEMA ---
2026-03-13 23:00:01 +01:00
if ( themeType = = AppThemeType . music ) . . . [
2026-03-14 00:00:01 +01:00
MusicCassetteCard ( title: loc . onlineTitle , subtitle: loc . onlineSub , neonColor: Colors . blueAccent , angle: - 0.04 , leftIcon: FontAwesomeIcons . sliders , rightIcon: FontAwesomeIcons . globe , themeType: themeType , onTap: ( ) { Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const LobbyScreen ( ) ) ) ; } ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 12 ) ,
2026-03-14 00:00:01 +01:00
MusicCassetteCard ( title: loc . cpuTitle , subtitle: loc . cpuSub , neonColor: Colors . purpleAccent , angle: 0.03 , leftIcon: FontAwesomeIcons . desktop , rightIcon: FontAwesomeIcons . music , themeType: themeType , onTap: ( ) = > _showMatchSetupDialog ( true ) ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 12 ) ,
2026-03-14 00:00:01 +01:00
MusicCassetteCard ( title: loc . localTitle , subtitle: loc . localSub , neonColor: Colors . deepPurpleAccent , angle: - 0.02 , leftIcon: FontAwesomeIcons . headphones , rightIcon: FontAwesomeIcons . headphones , themeType: themeType , onTap: ( ) = > _showMatchSetupDialog ( false ) ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 30 ) ,
Row (
2026-03-14 00:00:01 +01:00
mainAxisAlignment: MainAxisAlignment . spaceEvenly , crossAxisAlignment: CrossAxisAlignment . start ,
2026-03-13 23:00:01 +01:00
children: [
2026-03-14 00:00:01 +01:00
Expanded ( child: MusicKnobCard ( title: loc . leaderboardTitle , icon: FontAwesomeIcons . compactDisc , iconColor: Colors . amber , themeType: themeType , onTap: ( ) = > showDialog ( context: context , builder: ( ctx ) = > const LeaderboardDialog ( ) ) ) ) ,
Expanded ( child: MusicKnobCard ( title: loc . questsTitle , icon: FontAwesomeIcons . microphoneLines , themeType: themeType , onTap: ( ) = > showDialog ( context: context , builder: ( ctx ) = > const QuestsDialog ( ) ) ) ) ,
Expanded ( child: MusicKnobCard ( title: loc . themesTitle , icon: FontAwesomeIcons . palette , themeType: themeType , onTap: ( ) = > Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const SettingsScreen ( ) ) ) ) ) ,
Expanded ( child: MusicKnobCard ( title: loc . tutorialTitle , icon: FontAwesomeIcons . bookOpen , themeType: themeType , onTap: ( ) = > showDialog ( context: context , builder: ( ctx ) = > const TutorialDialog ( ) ) ) ) ,
2026-03-13 23:00:01 +01:00
] ,
) ,
] else . . . [
Column (
crossAxisAlignment: CrossAxisAlignment . stretch ,
children: [
2026-03-14 00:00:01 +01:00
_buildCyberCard ( FeatureCard ( title: loc . onlineTitle , subtitle: loc . onlineSub , icon: Icons . public , color: Colors . lightBlue . shade200 , theme: theme , themeType: themeType , isFeatured: true , onTap: ( ) { Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const LobbyScreen ( ) ) ) ; } ) , themeType ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 12 ) ,
2026-03-14 00:00:01 +01:00
_buildCyberCard ( FeatureCard ( title: loc . cpuTitle , subtitle: loc . cpuSub , icon: Icons . smart_toy , color: Colors . purple . shade200 , theme: theme , themeType: themeType , onTap: ( ) = > _showMatchSetupDialog ( true ) ) , themeType ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 12 ) ,
2026-03-14 00:00:01 +01:00
_buildCyberCard ( FeatureCard ( title: loc . localTitle , subtitle: loc . localSub , icon: Icons . people_alt , color: Colors . red . shade200 , theme: theme , themeType: themeType , onTap: ( ) = > _showMatchSetupDialog ( false ) ) , themeType ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 12 ) ,
2026-03-01 20:59:06 +01:00
2026-03-13 23:00:01 +01:00
Row (
children: [
2026-03-14 00:00:01 +01:00
Expanded ( child: _buildCyberCard ( FeatureCard ( title: loc . leaderboardTitle , subtitle: " Top 50 Globale " , icon: Icons . leaderboard , color: Colors . amber . shade200 , theme: theme , themeType: themeType , onTap: ( ) = > showDialog ( context: context , builder: ( ctx ) = > const LeaderboardDialog ( ) ) , compact: true ) , themeType ) ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( width: 12 ) ,
2026-03-14 00:00:01 +01:00
Expanded ( child: _buildCyberCard ( FeatureCard ( title: loc . questsTitle , subtitle: " Missioni " , icon: Icons . assignment_turned_in , color: Colors . green . shade200 , theme: theme , themeType: themeType , onTap: ( ) = > showDialog ( context: context , builder: ( ctx ) = > const QuestsDialog ( ) ) , compact: true ) , themeType ) ) ,
2026-03-13 23:00:01 +01:00
] ,
) ,
2026-02-27 23:35:54 +01:00
2026-03-13 23:00:01 +01:00
const SizedBox ( height: 12 ) ,
2026-03-01 20:59:06 +01:00
2026-03-13 23:00:01 +01:00
Row (
children: [
2026-03-14 00:00:01 +01:00
Expanded ( child: _buildCyberCard ( FeatureCard ( title: loc . themesTitle , subtitle: " Personalizza " , icon: Icons . palette , color: Colors . teal . shade200 , theme: theme , themeType: themeType , onTap: ( ) = > Navigator . push ( context , MaterialPageRoute ( builder: ( _ ) = > const SettingsScreen ( ) ) ) , compact: true ) , themeType ) ) ,
2026-03-13 23:00:01 +01:00
const SizedBox ( width: 12 ) ,
2026-03-14 00:00:01 +01:00
Expanded ( child: _buildCyberCard ( FeatureCard ( title: loc . tutorialTitle , subtitle: " Come giocare " , icon: Icons . school , color: Colors . indigo . shade200 , theme: theme , themeType: themeType , onTap: ( ) = > showDialog ( context: context , builder: ( ctx ) = > const TutorialDialog ( ) ) , compact: true ) , themeType ) ) ,
2026-03-13 23:00:01 +01:00
] ,
) ,
] ,
) ,
] ,
2026-03-01 20:59:06 +01:00
const SizedBox ( height: 10 ) ,
] ,
) ,
2026-02-27 23:35:54 +01:00
) ,
2026-03-01 20:59:06 +01:00
) ,
2026-02-27 23:35:54 +01:00
) ,
2026-03-01 20:59:06 +01:00
) ;
} ,
2026-02-27 23:35:54 +01:00
) ,
) ;
return Scaffold (
2026-03-13 23:00:01 +01:00
backgroundColor: themeType = = AppThemeType . doodle ? Colors . white : ( bgImage ! = null ? Colors . transparent : theme . background ) ,
2026-02-27 23:35:54 +01:00
body: Stack (
children: [
2026-03-13 23:00:01 +01:00
Container ( color: themeType = = AppThemeType . doodle ? Colors . white : theme . background ) ,
2026-03-14 00:00:01 +01:00
// Sfondo per il tema Doodle
2026-03-13 23:00:01 +01:00
if ( themeType = = AppThemeType . doodle )
2026-03-14 00:00:01 +01:00
Positioned . fill ( child: CustomPaint ( painter: DoodleBackgroundPainter ( fillColor: Colors . white , strokeColor: Colors . blue . withOpacity ( 0.15 ) , seed: 0 , isCircle: false ) ) ) ,
2026-02-27 23:35:54 +01:00
if ( bgImage ! = null )
2026-03-14 00:00:01 +01:00
Positioned . fill ( child: Image . asset ( bgImage , fit: BoxFit . cover , alignment: Alignment . center ) ) ,
if ( bgImage ! = null & & ( themeType = = AppThemeType . cyberpunk | | themeType = = AppThemeType . music ) )
Positioned . fill ( child: Container ( decoration: BoxDecoration ( gradient: LinearGradient ( begin: Alignment . topCenter , end: Alignment . bottomCenter , colors: [ Colors . black . withOpacity ( 0.4 ) , Colors . black . withOpacity ( 0.8 ) ] ) ) ) ) ,
2026-03-13 23:00:01 +01:00
2026-03-14 00:00:01 +01:00
// Cavi musicali per il tema Musica
2026-03-13 23:00:01 +01:00
if ( themeType = = AppThemeType . music )
2026-03-14 00:00:01 +01:00
Positioned . fill ( child: IgnorePointer ( child: CustomPaint ( painter: AudioCablesPainter ( ) ) ) ) ,
2026-03-13 23:00:01 +01:00
2026-02-27 23:35:54 +01:00
Positioned . fill ( child: uiContent ) ,
] ,
) ,
) ;
}
}