// =========================================================================== // 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'; import 'package:flutter/foundation.dart'; import 'dart:math' as math; import 'package:google_fonts/google_fonts.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../logic/game_controller.dart'; 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'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:tetraq/l10n/app_localizations.dart'; TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) { if (themeType == AppThemeType.doodle) { return GoogleFonts.permanentMarker(textStyle: baseStyle); } else if (themeType == AppThemeType.arcade) { return GoogleFonts.pressStart2p(textStyle: baseStyle.copyWith( fontSize: baseStyle.fontSize != null ? baseStyle.fontSize! * 0.75 : null, letterSpacing: 0.5, )); } else if (themeType == AppThemeType.grimorio) { return GoogleFonts.cinzelDecorative(textStyle: baseStyle.copyWith(fontWeight: FontWeight.bold)); } return baseStyle; } class _DoodleBackgroundPainter extends CustomPainter { final Color fillColor; final Color strokeColor; final int seed; final bool isCircle; _DoodleBackgroundPainter({required this.fillColor, required this.strokeColor, required this.seed, this.isCircle = false}); @override void paint(Canvas canvas, Size size) { final math.Random random = math.Random(seed); double wobble() => random.nextDouble() * 6 - 3; final Paint fillPaint = Paint() ..color = fillColor ..style = PaintingStyle.fill; final Paint strokePaint = Paint() ..color = strokeColor ..strokeWidth = 2.5 ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round ..strokeJoin = StrokeJoin.round; if (isCircle) { final Rect rect = Rect.fromLTWH(wobble(), wobble(), size.width + wobble(), size.height + wobble()); canvas.save(); canvas.translate(wobble(), wobble()); canvas.drawOval(rect, fillPaint); canvas.restore(); canvas.drawOval(rect, strokePaint); canvas.save(); canvas.translate(random.nextDouble() * 4 - 2, random.nextDouble() * 4 - 2); canvas.drawOval(rect, strokePaint..strokeWidth = 1.0..color = strokeColor.withOpacity(0.6)); canvas.restore(); } else { final Path path = Path(); path.moveTo(wobble(), wobble()); path.lineTo(size.width + wobble(), wobble()); path.lineTo(size.width + wobble(), size.height + wobble()); path.lineTo(wobble(), size.height + wobble()); path.close(); final Path fillPath = Path(); fillPath.moveTo(wobble() * 1.5, wobble() * 1.5); fillPath.lineTo(size.width + wobble() * 1.5, wobble() * 1.5); fillPath.lineTo(size.width + wobble() * 1.5, size.height + wobble() * 1.5); fillPath.lineTo(wobble() * 1.5, size.height + wobble() * 1.5); fillPath.close(); canvas.drawPath(fillPath, fillPaint); canvas.drawPath(path, strokePaint); canvas.save(); canvas.translate(random.nextDouble() * 3 - 1.5, random.nextDouble() * 3 - 1.5); canvas.drawPath(path, strokePaint..strokeWidth = 1.0..color = strokeColor.withOpacity(0.6)); canvas.restore(); } } @override bool shouldRepaint(covariant _DoodleBackgroundPainter oldDelegate) { return oldDelegate.fillColor != fillColor || oldDelegate.strokeColor != strokeColor; } } class _NeonShapeButton extends StatelessWidget { 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 }); 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( painter: _DoodleBackgroundPainter( fillColor: isSelected ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: label.length * 3, ), 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), Text(isLocked ? "Liv. 10" : label, style: _getTextStyle(themeType, TextStyle(color: inkColor, fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 0.5))), ], ), ), ), ), ); } Color mainColor = isSpecial && !isLocked ? Colors.purpleAccent : theme.playerBlue; return GestureDetector( onTap: isLocked ? null : onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 250), curve: Curves.easeOutCubic, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), transform: Matrix4.translationValues(0, isSelected ? 2 : 0, 0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), 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)], ), border: Border.all( color: isLocked ? Colors.transparent : (isSelected ? mainColor : Colors.white.withOpacity(0.1)), width: isSelected ? 2 : 1, ), 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)), ], ), 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), Text(isLocked ? "Liv. 10" : label, style: _getTextStyle(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))), ], ), ), ); } } class _NeonSizeButton extends StatelessWidget { final String label; final bool isSelected; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap; 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) { 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; return Transform.rotate( angle: tilt, child: GestureDetector( onTap: onTap, 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: _getTextStyle(themeType, TextStyle(color: inkColor, fontSize: 18, fontWeight: FontWeight.w900))), ), ), ), ), ); } return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 250), curve: Curves.easeOutCubic, width: 50, height: 50, transform: Matrix4.translationValues(0, isSelected ? 2 : 0, 0), decoration: BoxDecoration( shape: BoxShape.circle, 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)], ), border: Border.all( color: isSelected ? theme.playerRed : Colors.white.withOpacity(0.1), width: isSelected ? 2 : 1, ), 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)), ], ), child: Center( child: Text(label, style: _getTextStyle(themeType, TextStyle(color: isSelected ? Colors.white : theme.text.withOpacity(0.6), fontSize: 14, fontWeight: isSelected ? FontWeight.w900 : FontWeight.bold))), ), ), ); } } class _NeonTimeSwitch extends StatelessWidget { final bool isTimeMode; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap; const _NeonTimeSwitch({required this.isTimeMode, required this.theme, required this.themeType, required this.onTap}); @override Widget build(BuildContext context) { if (themeType == AppThemeType.doodle) { Color doodleColor = Colors.orange.shade200; Color inkColor = const Color(0xFF111122); return Transform.rotate( angle: -0.015, child: GestureDetector( onTap: onTap, child: CustomPaint( painter: _DoodleBackgroundPainter( fillColor: isTimeMode ? doodleColor : Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: 42, ), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ 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: _getTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900, fontSize: 16, letterSpacing: 2.0))), Text(isTimeMode ? '15 sec a mossa' : 'Nessun limite', style: _getTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 13, fontWeight: FontWeight.bold))), ], ), ], ), ), ), ), ); } return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), 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)], ), border: Border.all( color: isTimeMode ? Colors.amber : Colors.white.withOpacity(0.1), width: isTimeMode ? 2 : 1, ), 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)), ], ), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ 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: _getTextStyle(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: _getTextStyle(themeType, TextStyle(color: isTimeMode ? Colors.amber.shade200 : theme.text.withOpacity(0.4), fontSize: 11, fontWeight: FontWeight.bold))), ], ), ], ), ), ); } } class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State with WidgetsBindingObserver { int _debugTapCount = 0; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) { _checkPlayerName(); }); _checkClipboardForInvite(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { _checkClipboardForInvite(); } } void _checkPlayerName() { if (StorageService.instance.playerName.isEmpty) { _showNameDialog(); } } void _showNameDialog() { final TextEditingController nameController = TextEditingController(text: StorageService.instance.playerName); showDialog( context: context, barrierDismissible: false, barrierColor: Colors.black.withOpacity(0.8), builder: (context) { final themeManager = context.watch(); final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType; Color inkColor = const Color(0xFF111122); final loc = AppLocalizations.of(context)!; Widget dialogContent = themeType == AppThemeType.doodle ? CustomPaint( painter: _DoodleBackgroundPainter(fillColor: Colors.yellow.shade100, strokeColor: inkColor, seed: 100), child: Padding( padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 25.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(loc.welcomeTitle, style: _getTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900, fontSize: 28, letterSpacing: 2.0)), textAlign: TextAlign.center), const SizedBox(height: 20), Text('Scegli il tuo nome da battaglia', style: _getTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 18)), textAlign: TextAlign.center), const SizedBox(height: 30), TextField( controller: nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 5, style: _getTextStyle(themeType, TextStyle(color: inkColor, fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 8)), decoration: InputDecoration( hintText: loc.nameHint, hintStyle: _getTextStyle(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: 40), GestureDetector( onTap: () { final name = nameController.text.trim(); if (name.isNotEmpty) { StorageService.instance.savePlayerName(name); Navigator.of(context).pop(); setState(() {}); } }, child: CustomPaint( 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: _getTextStyle(themeType, TextStyle(color: inkColor, fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: 1.5))), ), ), ), ], ), ), ) : Container( 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)] ), child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 25.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(loc.welcomeTitle, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 24, letterSpacing: 1.5)), textAlign: TextAlign.center), const SizedBox(height: 20), Text('Scegli il tuo nome da battaglia per sfidare i tuoi amici online.', style: _getTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.8), fontSize: 16)), textAlign: TextAlign.center), const SizedBox(height: 40), TextField( controller: nameController, textCapitalization: TextCapitalization.characters, textAlign: TextAlign.center, maxLength: 5, style: _getTextStyle(themeType, TextStyle(color: theme.text, fontSize: 32, fontWeight: FontWeight.bold, letterSpacing: 8)), decoration: InputDecoration( hintText: loc.nameHint, hintStyle: _getTextStyle(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)), ), ), 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(); if (name.isNotEmpty) { StorageService.instance.savePlayerName(name); Navigator.of(context).pop(); setState(() {}); } }, child: Text(loc.saveAndPlay, style: _getTextStyle(themeType, const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 1.5))), ), ), ], ), ), ), ); if (themeType == AppThemeType.cyberpunk) dialogContent = _AnimatedCyberBorder(child: dialogContent); return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: dialogContent); }, ); } Future _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"); } } void _promptJoinRoom(String roomCode) { showDialog( context: context, builder: (context) { final theme = context.watch().currentColors; final themeType = context.read().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: _getTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.bold))), content: Text("Vuoi unirti alla stanza $roomCode?", style: _getTextStyle(themeType, TextStyle(color: theme.text))), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: Text("No", style: _getTextStyle(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: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : Colors.white, fontWeight: FontWeight.bold))), ), ], ); } ); } void _showMatchSetupDialog(bool isVsCPU) { int localRadius = 4; ArenaShape localShape = ArenaShape.classic; bool localTimeMode = true; bool isChaosUnlocked = StorageService.instance.playerLevel >= 10; final loc = AppLocalizations.of(context)!; showDialog( context: context, barrierColor: Colors.black.withOpacity(0.8), builder: (ctx) { final themeManager = ctx.watch(); 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(isVsCPU ? loc.cpuTitle : loc.localTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 26, fontWeight: FontWeight.w900, color: inkColor, letterSpacing: 2))), const SizedBox(height: 25), Text("FORMA ARENA", style: _getTextStyle(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: _getTextStyle(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", style: _getTextStyle(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), Transform.rotate( angle: -0.02, child: GestureDetector( onTap: () { Navigator.pop(ctx); context.read().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: _getTextStyle(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 ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5), boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? [] : [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(isVsCPU ? loc.cpuTitle : loc.localTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2))), const SizedBox(height: 20), Text("FORMA ARENA", style: _getTextStyle(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: _getTextStyle(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", style: _getTextStyle(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), 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))), onPressed: () { Navigator.pop(ctx); context.read().startNewGame(localRadius, vsCPU: isVsCPU, shape: localShape, timeMode: localTimeMode); Navigator.push(context, MaterialPageRoute(builder: (_) => const GameScreen())); }, child: Text(loc.startGame, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900, letterSpacing: 2)), ), ) ], ), ), ), ); if (themeType == AppThemeType.cyberpunk) { dialogContent = _AnimatedCyberBorder(child: dialogContent); } return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20), child: dialogContent); }, ); } ); } // --- NUOVA FUNZIONE: MOSTRA LE SFIDE GIORNALIERE --- Future _showDailyQuestsDialog() async { final prefs = await SharedPreferences.getInstance(); showDialog( context: context, barrierColor: Colors.black.withOpacity(0.8), builder: (ctx) { final themeManager = ctx.watch(); final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType; Color inkColor = const Color(0xFF111122); final loc = AppLocalizations.of(context)!; return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: Container( padding: const EdgeInsets.all(25.0), 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: Border.all(color: theme.playerBlue.withOpacity(0.5), width: 2), boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.2), blurRadius: 20, spreadRadius: 5)] ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.assignment_turned_in, size: 50, color: theme.playerBlue), const SizedBox(height: 10), Text(loc.questsTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 22, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))), const SizedBox(height: 25), // Generiamo dinamicamente le 3 missioni salvate in memoria ...List.generate(3, (index) { int i = index + 1; int type = prefs.getInt('q${i}_type') ?? 0; int prog = prefs.getInt('q${i}_prog') ?? 0; int target = prefs.getInt('q${i}_target') ?? 1; String title = ""; IconData icon = Icons.star; if (type == 0) { title = "Vinci partite Online"; icon = Icons.public; } else if (type == 1) { title = "Vinci contro la CPU"; icon = Icons.smart_toy; } else { title = "Gioca in Arene Speciali"; icon = Icons.extension; } bool completed = prog >= target; double percent = (prog / target).clamp(0.0, 1.0); return Container( margin: const EdgeInsets.only(bottom: 15), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: completed ? Colors.green.withOpacity(0.1) : theme.text.withOpacity(0.05), borderRadius: BorderRadius.circular(15), border: Border.all(color: completed ? Colors.green : theme.gridLine.withOpacity(0.3)), ), child: Row( children: [ Icon(icon, color: completed ? Colors.green : theme.text.withOpacity(0.6), size: 30), const SizedBox(width: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: _getTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: completed ? Colors.green : theme.text))), const SizedBox(height: 6), ClipRRect( borderRadius: BorderRadius.circular(10), child: LinearProgressIndicator( value: percent, backgroundColor: theme.gridLine.withOpacity(0.2), color: completed ? Colors.green : theme.playerBlue, minHeight: 8, ), ) ], ), ), const SizedBox(width: 10), Text("$prog / $target", style: _getTextStyle(themeType, TextStyle(fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6)))), ], ), ); }), const SizedBox(height: 15), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), onPressed: () => Navigator.pop(ctx), child: const Text("CHIUDI", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2)), ), ) ], ), ), ); } ); } // --- NUOVA FUNZIONE: MOSTRA LA CLASSIFICA GLOBALE --- void _showLeaderboardDialog() { showDialog( context: context, barrierColor: Colors.black.withOpacity(0.8), builder: (ctx) { final themeManager = ctx.watch(); final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType; final loc = AppLocalizations.of(context)!; Widget content = Container( padding: const EdgeInsets.all(20.0), 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: Border.all(color: Colors.amber.withOpacity(0.8), width: 2), boxShadow: [BoxShadow(color: Colors.amber.withOpacity(0.2), blurRadius: 20, spreadRadius: 5)] ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.emoji_events, size: 50, color: Colors.amber), const SizedBox(height: 10), Text(loc.leaderboardTitle, style: _getTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 1.5))), const SizedBox(height: 20), // Lista giocatori pescata da Firebase! SizedBox( height: 350, child: StreamBuilder( stream: FirebaseFirestore.instance.collection('leaderboard').orderBy('xp', descending: true).limit(50).snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator(color: theme.playerBlue)); } if (!snapshot.hasData || snapshot.data!.docs.isEmpty) { return Center(child: Text("Ancora nessun campione...", style: TextStyle(color: theme.text.withOpacity(0.5)))); } final docs = snapshot.data!.docs; return ListView.builder( physics: const BouncingScrollPhysics(), itemCount: docs.length, // <--- ECCO LA RIGA MAGICA AGGIUNTA! itemBuilder: (context, index) { var data = docs[index].data() as Map; String? myUid = FirebaseAuth.instance.currentUser?.uid; bool isMe = docs[index].id == myUid; return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: isMe ? theme.playerBlue.withOpacity(0.2) : theme.text.withOpacity(0.05), borderRadius: BorderRadius.circular(10), border: isMe ? Border.all(color: theme.playerBlue, width: 1.5) : null ), child: Row( children: [ Text("#${index + 1}", style: _getTextStyle(themeType, TextStyle(fontWeight: FontWeight.w900, color: index == 0 ? Colors.amber : (index == 1 ? Colors.grey.shade400 : (index == 2 ? Colors.brown.shade300 : theme.text.withOpacity(0.5)))))), const SizedBox(width: 15), Expanded(child: Text(data['name'] ?? 'Unknown', style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: isMe ? FontWeight.w900 : FontWeight.bold, color: theme.text)))), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text("Lv. ${data['level'] ?? 1}", style: TextStyle(color: theme.playerRed, fontWeight: FontWeight.bold, fontSize: 12)), Text("${data['xp'] ?? 0} XP", style: TextStyle(color: theme.text.withOpacity(0.6), fontSize: 10)), ], ) ], ), ); } ); } ), ), const SizedBox(height: 15), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.amber.shade700, foregroundColor: Colors.black, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), onPressed: () => Navigator.pop(ctx), child: const Text("CHIUDI", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2)), ), ) ], ), ); if (themeType == AppThemeType.cyberpunk) content = _AnimatedCyberBorder(child: content); return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.all(20), child: content); } ); } void _showTutorialDialog() { showDialog( context: context, barrierColor: Colors.black.withOpacity(0.8), builder: (ctx) { final themeManager = ctx.watch(); final theme = themeManager.currentColors; final themeType = themeManager.currentThemeType; Color inkColor = const Color(0xFF111122); final loc = AppLocalizations.of(context)!; String goldLabel = themeType == AppThemeType.grimorio ? "CORONA:" : "ORO:"; String bombLabel = themeType == AppThemeType.grimorio ? "STREGA:" : "BOMBA:"; String jokerLabel = themeType == AppThemeType.grimorio ? "GIULLARE:" : "JOLLY:"; Widget dialogContent = themeType == AppThemeType.doodle ? Transform.rotate( angle: -0.01, child: CustomPaint( painter: _DoodleBackgroundPainter(fillColor: Colors.yellow.shade50, strokeColor: inkColor, seed: 400), child: Padding( padding: const EdgeInsets.all(25.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center(child: Text("COME GIOCARE", style: _getTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: inkColor, letterSpacing: 2)))), const SizedBox(height: 20), _TutorialStep(icon: Icons.line_axis, text: "Chiudi i 4 lati di un quadrato e conquisti 1 punto e avere una mossa extra!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 15), _TutorialStep(icon: Icons.lens_blur, text: "Ma presta attenzione! ogni quadrato nasconde un insidia o un regalo!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 15), const Divider(color: Colors.black26, thickness: 2), const SizedBox(height: 10), Center(child: Text("GLOSSARIO ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 18, fontWeight: FontWeight.w900, color: inkColor)))), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.gold(themeType), iconColor: Colors.amber.shade700, text: "$goldLabel Chiudilo per ottenere +2 Punti.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "SCAMBIO: Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: Colors.green.shade600, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "GHIACCIO: Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "x2: Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "BUCO NERO: Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 25), Center( child: GestureDetector( onTap: () => Navigator.pop(ctx), child: CustomPaint( painter: _DoodleBackgroundPainter(fillColor: Colors.red.shade200, strokeColor: inkColor, seed: 401), child: Container( height: 50, width: 150, alignment: Alignment.center, child: Text("HO CAPITO!", style: _getTextStyle(themeType, TextStyle(fontSize: 18, fontWeight: FontWeight.w900, color: inkColor))), ), ), ), ) ], ), ), ), ) : Container( padding: const EdgeInsets.all(25.0), 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 ? null : Border.all(color: Colors.white.withOpacity(0.15), width: 1.5), boxShadow: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? [] : [BoxShadow(color: Colors.black.withOpacity(0.5), blurRadius: 20, offset: const Offset(4, 10))], ), child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Center(child: Text("COME GIOCARE", style: _getTextStyle(themeType, TextStyle(fontSize: 24, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2)))), const SizedBox(height: 20), _TutorialStep(icon: Icons.grid_4x4, text: "Chiudi i 4 lati di un quadrato e conquisti 1 punto e avere una mossa extra!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 15), _TutorialStep(icon: Icons.lens_blur, text: "Ma presta attenzione! ogni quadrato nasconde un insidia o un regalo!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 15), const Divider(color: Colors.white24, thickness: 1.5), const SizedBox(height: 10), Center(child: Text("GLOSSARIO ARENA", style: _getTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.w900, color: theme.text.withOpacity(0.7), letterSpacing: 1.5)))), const SizedBox(height: 15), _TutorialStep(icon: ThemeIcons.gold(themeType), iconColor: Colors.amber, text: "$goldLabel Chiudilo per ottenere +2 Punti.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.bomb(themeType), iconColor: themeType == AppThemeType.cyberpunk || themeType == AppThemeType.arcade ? Colors.greenAccent : Colors.deepPurple, text: "$bombLabel Non chiuderlo! Perderai -1 Punto.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.swap(themeType), iconColor: Colors.purpleAccent, text: "SCAMBIO: Inverte istantaneamente i punteggi dei giocatori.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.joker(themeType), iconColor: theme.playerBlue, text: "$jokerLabel Scegli dove nasconderlo a inizio partita. Se lo chiudi tu +2, se lo chiude l'avversario -1!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.ice(themeType), iconColor: Colors.cyanAccent, text: "GHIACCIO: Devi cliccarlo due volte per poterlo rompere e chiudere.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.multiplier(themeType), iconColor: Colors.yellowAccent, text: "x2: Non dà punti, ma raddoppia il punteggio della prossima casella che chiudi!", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 10), _TutorialStep(icon: ThemeIcons.block(themeType), iconColor: Colors.grey, text: "BUCO NERO: Questa casella non esiste. Se la chiudi perdi il turno.", themeType: themeType, inkColor: inkColor, theme: theme), const SizedBox(height: 30), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: theme.playerBlue, foregroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))), onPressed: () => Navigator.pop(ctx), child: const Text("CHIUDI", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 2)), ), ) ], ), ), ); if (themeType == AppThemeType.cyberpunk) dialogContent = _AnimatedCyberBorder(child: dialogContent); return Dialog(backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: dialogContent); }, ); } Widget _buildCyberCard(Widget card, AppThemeType themeType) { if (themeType == AppThemeType.cyberpunk) { return _AnimatedCyberBorder(child: card); } return card; } @override Widget build(BuildContext context) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); final themeManager = context.watch(); final themeType = themeManager.currentThemeType; final theme = themeManager.currentColors; Color inkColor = const Color(0xFF111122); final loc = AppLocalizations.of(context)!; String? bgImage; if (themeType == AppThemeType.wood) bgImage = 'assets/images/wood_bg.jpg'; if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg'; if (themeType == AppThemeType.cyberpunk) bgImage = 'assets/images/cyber_bg.jpg'; int wins = StorageService.instance.wins; int losses = StorageService.instance.losses; String playerName = StorageService.instance.playerName; if (playerName.isEmpty) playerName = "GUEST"; int level = StorageService.instance.playerLevel; int currentXP = StorageService.instance.totalXP; double xpProgress = (currentXP % 100) / 100.0; Widget uiContent = SafeArea( child: LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( physics: const BouncingScrollPhysics(), child: ConstrainedBox( constraints: BoxConstraints( minHeight: constraints.maxHeight, ), child: IntrinsicHeight( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( onTap: _showNameDialog, child: Row( children: [ themeType == AppThemeType.doodle ? CustomPaint( painter: _DoodleBackgroundPainter(fillColor: Colors.white.withOpacity(0.8), strokeColor: inkColor, seed: 1, isCircle: true), 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: [ Text(playerName, style: _getTextStyle(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: _getTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? inkColor.withOpacity(0.8) : theme.playerBlue, fontSize: 14, fontWeight: FontWeight.bold, letterSpacing: 1))), ], ), ], ), ), GestureDetector( onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const HistoryScreen())), child: themeType == AppThemeType.doodle ? Transform.rotate( angle: 0.04, child: CustomPaint( painter: _DoodleBackgroundPainter(fillColor: Colors.yellow.shade100, strokeColor: inkColor, seed: 2), 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), Text("$wins", style: _getTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900))), const SizedBox(width: 12), Icon(Icons.sentiment_very_dissatisfied, color: inkColor, size: 20), const SizedBox(width: 6), Text("$losses", style: _getTextStyle(themeType, TextStyle(color: inkColor, fontWeight: FontWeight.w900))), ], ), ), ), ) : 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: [ Icon(Icons.emoji_events, color: Colors.amber.shade600, size: 20), const SizedBox(width: 6), Text("$wins", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.w900))), const SizedBox(width: 12), Icon(Icons.sentiment_very_dissatisfied, color: theme.playerRed.withOpacity(0.8), size: 20), const SizedBox(width: 6), Text("$losses", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.w900))), ], ), ), ) ], ), const Spacer(), Center( child: Transform.rotate( angle: themeType == AppThemeType.doodle ? -0.04 : 0, // --- IL TRUCCO DELLO SVILUPPATORE PROTETTO --- child: GestureDetector( onTap: () { if (kReleaseMode) return; _debugTapCount++; if (_debugTapCount >= 5) { _debugTapCount = 0; StorageService.instance.addXP(2000); setState(() {}); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("🛠 DEBUG MODE: +20 Livelli! Tutto sbloccato.", style: _getTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))), backgroundColor: Colors.purpleAccent, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), ) ); } }, child: FittedBox( fit: BoxFit.scaleDown, child: Text( loc.appTitle.toUpperCase(), style: _getTextStyle(themeType, TextStyle( fontSize: 65, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? inkColor : theme.text, letterSpacing: 10, shadows: themeType == AppThemeType.doodle || themeType == AppThemeType.arcade ? [] : [ 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), ] )) ), ), ), ), ), const Spacer(), // --- NUOVA LISTA BOTTONI CON CLASSIFICHE E SFIDE --- Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _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), const SizedBox(height: 12), _buildCyberCard(_FeatureCard(title: loc.cpuTitle, subtitle: loc.cpuSub, icon: Icons.smart_toy, color: Colors.purple.shade200, theme: theme, themeType: themeType, onTap: () => _showMatchSetupDialog(true)), themeType), const SizedBox(height: 12), _buildCyberCard(_FeatureCard(title: loc.localTitle, subtitle: loc.localSub, icon: Icons.people_alt, color: Colors.red.shade200, theme: theme, themeType: themeType, onTap: () => _showMatchSetupDialog(false)), themeType), const SizedBox(height: 12), // NUOVI BOTTONI PER LA VERSIONE 2.0 Row( children: [ Expanded(child: _buildCyberCard(_FeatureCard(title: loc.leaderboardTitle, subtitle: "Top 50 Globale", icon: Icons.leaderboard, color: Colors.amber.shade200, theme: theme, themeType: themeType, onTap: _showLeaderboardDialog, compact: true), themeType)), const SizedBox(width: 12), Expanded(child: _buildCyberCard(_FeatureCard(title: loc.questsTitle, subtitle: "Missioni", icon: Icons.assignment_turned_in, color: Colors.green.shade200, theme: theme, themeType: themeType, onTap: _showDailyQuestsDialog, compact: true), themeType)), ], ), const SizedBox(height: 12), Row( children: [ 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)), const SizedBox(width: 12), Expanded(child: _buildCyberCard(_FeatureCard(title: loc.tutorialTitle, subtitle: "Come giocare", icon: Icons.school, color: Colors.indigo.shade200, theme: theme, themeType: themeType, onTap: _showTutorialDialog, compact: true), themeType)), ], ), ], ), const SizedBox(height: 10), ], ), ), ), ), ); }, ), ); return Scaffold( backgroundColor: bgImage != null ? Colors.transparent : theme.background, body: Stack( children: [ Container(color: theme.background), if (bgImage != null) Positioned.fill( child: Image.asset(bgImage, fit: BoxFit.cover, alignment: Alignment.center), ), if (bgImage != null) Positioned.fill( child: themeType == AppThemeType.cyberpunk ? Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.black.withOpacity(0.2), Colors.black.withOpacity(0.7)] ) ), ) : const SizedBox(), ), Positioned.fill(child: uiContent), ], ), ); } } class _TutorialStep extends StatelessWidget { final IconData icon; final Color? iconColor; final String text; final AppThemeType themeType; final Color inkColor; final ThemeColors theme; const _TutorialStep({required this.icon, this.iconColor, required this.text, required this.themeType, required this.inkColor, required this.theme}); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, color: iconColor ?? (themeType == AppThemeType.doodle ? inkColor : theme.playerBlue), size: 28), const SizedBox(width: 15), Expanded( child: Text(text, style: _getTextStyle(themeType, TextStyle(fontSize: 14, color: themeType == AppThemeType.doodle ? inkColor : theme.text.withOpacity(0.8), height: 1.3))), ), ], ); } } class _FeatureCard extends StatelessWidget { final String title; final String subtitle; final IconData icon; final Color color; final ThemeColors theme; final AppThemeType themeType; final VoidCallback onTap; final bool isFeatured; final bool compact; const _FeatureCard({required this.title, required this.subtitle, required this.icon, required this.color, required this.theme, required this.themeType, required this.onTap, this.isFeatured = false, this.compact = false}); @override Widget build(BuildContext context) { if (themeType == AppThemeType.doodle) { double tilt = (title.length % 2 == 0) ? -0.015 : 0.02; Color inkColor = const Color(0xFF111122); return Transform.rotate( angle: tilt, child: GestureDetector( onTap: onTap, child: CustomPaint( painter: _DoodleBackgroundPainter( fillColor: color, strokeColor: inkColor, seed: title.length * 5, ), child: Padding( padding: EdgeInsets.symmetric(horizontal: compact ? 12.0 : 22.0, vertical: compact ? 12.0 : 16.0), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon(icon, color: inkColor, size: compact ? 24 : 32), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ // --- MAGIA QUI: FittedBox impedisce di andare a capo --- FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text(title, style: _getTextStyle(themeType, TextStyle(color: inkColor, fontSize: compact ? 16 : 24, fontWeight: FontWeight.w900))), ), if (!compact) ...[ const SizedBox(height: 2), Text(subtitle, style: _getTextStyle(themeType, TextStyle(color: inkColor.withOpacity(0.8), fontSize: 14, fontWeight: FontWeight.bold))), ] ], ), ), if (!compact) Icon(Icons.chevron_right_rounded, color: inkColor.withOpacity(0.6), size: 32), ], ), ), ), ), ); } return GestureDetector( onTap: onTap, child: Container( padding: EdgeInsets.symmetric(horizontal: compact ? 12.0 : 20.0, vertical: compact ? 10.0 : 14.0), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isFeatured ? [color.withOpacity(0.9), color.withOpacity(0.6)] : [color.withOpacity(0.25), color.withOpacity(0.05)], ), borderRadius: BorderRadius.circular(15), border: Border.all(color: color.withOpacity(isFeatured ? 0.5 : 0.2), width: 1.5), boxShadow: [ BoxShadow(color: Colors.black.withOpacity(0.6), offset: const Offset(0, 8), blurRadius: 15), BoxShadow(color: color.withOpacity(isFeatured ? 0.3 : 0.05), offset: const Offset(-1, -1), blurRadius: 5), ] ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( padding: EdgeInsets.all(compact ? 6 : 10), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.white.withOpacity(0.3), Colors.white.withOpacity(0.05)], ), shape: BoxShape.circle, border: Border.all(color: Colors.white.withOpacity(0.2)), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.2), blurRadius: 5, offset: const Offset(2, 4))] ), child: Icon(icon, color: isFeatured ? Colors.white : color, size: compact ? 20 : 26), ), SizedBox(width: compact ? 10 : 20), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ // --- MAGIA QUI: FittedBox impedisce di andare a capo --- FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.centerLeft, child: Text(title, style: _getTextStyle(themeType, TextStyle(color: isFeatured ? Colors.white : theme.text, fontSize: compact ? 14 : 18, fontWeight: FontWeight.w900, shadows: [Shadow(color: Colors.black.withOpacity(0.5), offset: const Offset(1, 2), blurRadius: 2)]))), ), if (!compact) ...[ const SizedBox(height: 2), Text(subtitle, style: _getTextStyle(themeType, TextStyle(color: isFeatured ? Colors.white.withOpacity(0.8) : theme.text.withOpacity(0.6), fontSize: 12, fontWeight: FontWeight.bold))), ] ], ), ), if (!compact) Icon(Icons.chevron_right_rounded, color: isFeatured ? Colors.white.withOpacity(0.7) : color.withOpacity(0.5), size: 30), ], ), ), ); } } class _AnimatedCyberBorder extends StatefulWidget { final Widget child; const _AnimatedCyberBorder({required this.child}); @override State<_AnimatedCyberBorder> createState() => _AnimatedCyberBorderState(); } class _AnimatedCyberBorderState extends State<_AnimatedCyberBorder> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 3))..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = context.watch().currentColors; return AnimatedBuilder( animation: _controller, builder: (context, child) { return CustomPaint( painter: _CyberBorderPainter(animationValue: _controller.value, color1: theme.playerBlue, color2: theme.playerRed), child: Container( decoration: BoxDecoration(color: theme.background.withOpacity(0.9), borderRadius: BorderRadius.circular(15), boxShadow: [BoxShadow(color: theme.playerBlue.withOpacity(0.3), blurRadius: 25, spreadRadius: 2)]), padding: const EdgeInsets.all(3), child: widget.child, ), ); }, child: widget.child, ); } } class _CyberBorderPainter extends CustomPainter { final double animationValue; final Color color1; final Color color2; _CyberBorderPainter({required this.animationValue, required this.color1, required this.color2}); @override void paint(Canvas canvas, Size size) { final rect = Offset.zero & size; final RRect rrect = RRect.fromRectAndRadius(rect, const Radius.circular(15)); final Paint paint = Paint() ..shader = SweepGradient(colors: [color1, color2, color1, color2, color1], stops: const [0.0, 0.25, 0.5, 0.75, 1.0], transform: GradientRotation(animationValue * 2 * math.pi)).createShader(rect) ..style = PaintingStyle.stroke ..strokeWidth = 4.0 ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 4); canvas.drawRRect(rrect, paint); } @override bool shouldRepaint(covariant _CyberBorderPainter oldDelegate) => oldDelegate.animationValue != animationValue; }