diff --git a/lib/pantallas/pantalla_fin_partida.dart b/lib/pantallas/pantalla_fin_partida.dart index b0278cd..73ef07f 100644 --- a/lib/pantallas/pantalla_fin_partida.dart +++ b/lib/pantallas/pantalla_fin_partida.dart @@ -34,7 +34,10 @@ class _PantallaFinPartidaState extends State { @override void initState() { super.initState(); - _confetti = ConfettiController(duration: const Duration(seconds: 3)); + _confetti = ConfettiController(duration: const Duration(seconds: 5)); + Future.delayed(const Duration(milliseconds: 450), () { + if (mounted) _confetti.play(); + }); } @override @@ -56,6 +59,7 @@ class _PantallaFinPartidaState extends State { return Scaffold( extendBodyBehindAppBar: true, + backgroundColor: const Color(0xFF05070D), body: FondoFarolero( intenso: true, child: Stack( @@ -76,9 +80,9 @@ class _PantallaFinPartidaState extends State { child: ConfettiWidget( confettiController: _confetti, blastDirectionality: BlastDirectionality.explosive, - emissionFrequency: 0.06, - numberOfParticles: 18, - gravity: 0.22, + emissionFrequency: 0.09, + numberOfParticles: 28, + gravity: 0.18, colors: const [ TemaApp.colorDorado, TemaApp.colorNaranja, @@ -87,6 +91,24 @@ class _PantallaFinPartidaState extends State { ], ), ), + Positioned.fill( + child: IgnorePointer( + child: DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.transparent, + Colors.black.withValues(alpha: 0.10), + Colors.black.withValues(alpha: 0.52), + ], + stops: const [0.0, 0.54, 1.0], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ), + ), SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 22, 20, 28), @@ -103,46 +125,52 @@ class _PantallaFinPartidaState extends State { ? TemaApp.colorVerde : TemaApp.colorAcento, ), - const SizedBox(height: 18), - if (_progreso == null) - const _TarjetaRecompensaCargando() - else - _TarjetaProgresoGamificacion(progreso: _progreso!), - const SizedBox(height: 16), - _TarjetaSecreto( - palabra: partida.palabraSecreta, - categoria: BancoPalabras.nombreBonitoCategoria( - partida.categoriaReal, - l10n, + Transform.translate( + offset: const Offset(0, -18), + child: Column( + children: [ + if (_progreso == null) + const _TarjetaRecompensaCargando() + else + _TarjetaProgresoGamificacion(progreso: _progreso!), + const SizedBox(height: 18), + _TarjetaSecreto( + palabra: partida.palabraSecreta, + categoria: BancoPalabras.nombreBonitoCategoria( + partida.categoriaReal, + l10n, + ), + ), + const SizedBox(height: 18), + _TarjetaImpostores( + titulo: impostores.length == 1 + ? l10n.theImpostorWas + : l10n.theImpostorsWere, + impostores: impostores, + ), + const SizedBox(height: 18), + if (partida.historialVotaciones.isNotEmpty) + _TarjetaHistorialVotos(partida: partida, l10n: l10n), + const SizedBox(height: 24), + _BotonesFinPartida( + estado: estado, + onPrincipal: () async { + await context.read().desconectar(); + estado.limpiar(); + if (!context.mounted) return; + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute( + builder: (_) => const PantallaPrincipal(), + ), + (route) => false, + ); + }, + ), + const SizedBox(height: 16), + ], ), ), - const SizedBox(height: 16), - _TarjetaImpostores( - titulo: impostores.length == 1 - ? l10n.theImpostorWas - : l10n.theImpostorsWere, - impostores: impostores, - ), - const SizedBox(height: 16), - if (partida.historialVotaciones.isNotEmpty) - _TarjetaHistorialVotos(partida: partida, l10n: l10n), - const SizedBox(height: 24), - _BotonesFinPartida( - estado: estado, - onPrincipal: () async { - await context.read().desconectar(); - estado.limpiar(); - if (!context.mounted) return; - Navigator.pushAndRemoveUntil( - context, - MaterialPageRoute( - builder: (_) => const PantallaPrincipal(), - ), - (route) => false, - ); - }, - ), - const SizedBox(height: 16), ], ), ), @@ -192,8 +220,9 @@ class _HeroResultado extends StatelessWidget { @override Widget build(BuildContext context) { + final apertura = String.fromCharCode(0x00A1); final tituloLimpio = titulo - .replaceAll('¡', '') + .replaceAll(apertura, '') .replaceAll('!', '') .trim() .toUpperCase(); @@ -201,7 +230,7 @@ class _HeroResultado extends StatelessWidget { alignment: Alignment.center, children: [ SizedBox( - height: 330, + height: 420, width: double.infinity, child: CustomPaint(painter: _HeroCinematicoPainter(color: color)), ), @@ -212,6 +241,7 @@ class _HeroResultado extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: TemaApp.colorDorado, + fontSize: 38, fontWeight: FontWeight.w900, letterSpacing: -0.5, shadows: [ @@ -222,58 +252,54 @@ class _HeroResultado extends StatelessWidget { ], ), ).animate().fadeIn(duration: 260.ms).slideY(begin: -0.18), - const SizedBox(height: 42), + const SizedBox(height: 62), Text( 'RESULTADOS', style: Theme.of(context).textTheme.labelLarge?.copyWith( color: TemaApp.colorDorado, fontWeight: FontWeight.w900, - letterSpacing: 4, + letterSpacing: 5, ), ).animate().fadeIn(duration: 350.ms).slideY(begin: -0.25), - const SizedBox(height: 18), - Container( - width: 132, - height: 132, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: RadialGradient( - colors: [ - color.withValues(alpha: 0.55), - TemaApp.colorSuperficie, - Colors.black.withValues(alpha: 0.72), - ], + const SizedBox(height: 20), + Stack( + alignment: Alignment.center, + children: [ + Image.asset( + 'assets/rewards/medal_unlock_burst.png', + width: 210, + height: 210, + fit: BoxFit.cover, ), - border: Border.all(color: TemaApp.colorDorado, width: 3), - boxShadow: [ - BoxShadow( - color: color.withValues(alpha: 0.72), - blurRadius: 38, - spreadRadius: 4, - ), - BoxShadow( - color: TemaApp.colorDorado.withValues(alpha: 0.32), - blurRadius: 22, - spreadRadius: 1, - ), - ], - ), - child: Stack( - alignment: Alignment.center, - children: [ - Icon(icono, size: 72, color: TemaApp.colorDorado), - Positioned.fill( - child: DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: Colors.white.withValues(alpha: 0.08), - ), - ), + Container( + width: 154, + height: 154, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: RadialGradient( + colors: [ + color.withValues(alpha: 0.36), + const Color(0xFF111116), + Colors.black.withValues(alpha: 0.88), + ], ), + border: Border.all(color: TemaApp.colorDorado, width: 4), + boxShadow: [ + BoxShadow( + color: TemaApp.colorNaranja.withValues(alpha: 0.75), + blurRadius: 52, + spreadRadius: 7, + ), + BoxShadow( + color: color.withValues(alpha: 0.62), + blurRadius: 36, + spreadRadius: 2, + ), + ], ), - ], - ), + child: _IconoResultadoPremium(icono: icono), + ), + ], ) .animate() .scale( @@ -282,17 +308,17 @@ class _HeroResultado extends StatelessWidget { curve: Curves.elasticOut, ) .shimmer(delay: 700.ms, duration: 1500.ms), - const SizedBox(height: 14), + const SizedBox(height: 12), Text( - '¡$tituloLimpio!', + '$apertura$tituloLimpio!', textAlign: TextAlign.center, style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: color, - fontSize: 31, + fontSize: 34, fontWeight: FontWeight.w900, letterSpacing: 1.2, shadows: [ - Shadow(color: color.withValues(alpha: 0.85), blurRadius: 22), + Shadow(color: color.withValues(alpha: 0.90), blurRadius: 24), ], ), ).animate().fadeIn(delay: 180.ms).slideY(begin: 0.25), @@ -303,6 +329,47 @@ class _HeroResultado extends StatelessWidget { } } +class _IconoResultadoPremium extends StatelessWidget { + final IconData icono; + + const _IconoResultadoPremium({required this.icono}); + + @override + Widget build(BuildContext context) { + if (icono != Icons.theater_comedy) { + return Icon(icono, size: 82, color: TemaApp.colorDorado); + } + + return Stack( + alignment: Alignment.center, + children: [ + Transform.translate( + offset: const Offset(-18, 12), + child: Transform.rotate( + angle: -0.10, + child: Icon( + Icons.mood, + size: 66, + color: TemaApp.colorDorado.withValues(alpha: 0.98), + ), + ), + ), + Transform.translate( + offset: const Offset(20, -13), + child: Transform.rotate( + angle: 0.12, + child: Icon( + Icons.sentiment_dissatisfied, + size: 70, + color: TemaApp.colorDorado.withValues(alpha: 0.98), + ), + ), + ), + ], + ); + } +} + class _TarjetaProgresoGamificacion extends StatelessWidget { final ProgresoGamificacionUsuario progreso; @@ -320,15 +387,42 @@ class _TarjetaProgresoGamificacion extends StatelessWidget { children: [ Row( children: [ - const Icon(Icons.local_fire_department, color: TemaApp.colorNaranja), - const SizedBox(width: 8), + Container( + width: 52, + height: 52, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + TemaApp.colorNaranja.withValues(alpha: 0.95), + TemaApp.colorDorado.withValues(alpha: 0.78), + ], + begin: Alignment.bottomLeft, + end: Alignment.topRight, + ), + boxShadow: [ + BoxShadow( + color: TemaApp.colorNaranja.withValues(alpha: 0.42), + blurRadius: 22, + ), + ], + ), + child: const Icon( + Icons.local_fire_department, + color: Color(0xFF1B1010), + size: 30, + ), + ), + const SizedBox(width: 14), Expanded( child: Text( 'RECOMPENSAS DE PARTIDA', style: Theme.of(context).textTheme.titleMedium?.copyWith( color: TemaApp.colorDorado, + fontSize: 20, fontWeight: FontWeight.w900, - letterSpacing: 1, + letterSpacing: 1.1, + height: 1.05, ), ), ), @@ -337,7 +431,7 @@ class _TarjetaProgresoGamificacion extends StatelessWidget { ), const SizedBox(height: 16), _BarraFuegoPremium(antes: antes, despues: despues), - const SizedBox(height: 16), + const SizedBox(height: 20), if (nuevas.isEmpty) Text( 'Sin medallas nuevas esta vez. Seguí acumulando fuego.', @@ -408,10 +502,10 @@ class _PanelRecompensa extends StatelessWidget { @override Widget build(BuildContext context) { return ClipRRect( - borderRadius: BorderRadius.circular(28), + borderRadius: BorderRadius.circular(32), child: Container( width: double.infinity, - padding: const EdgeInsets.all(20), + padding: const EdgeInsets.all(24), decoration: BoxDecoration( gradient: LinearGradient( colors: [ @@ -421,23 +515,23 @@ class _PanelRecompensa extends StatelessWidget { begin: Alignment.topLeft, end: Alignment.bottomRight, ), - borderRadius: BorderRadius.circular(28), + borderRadius: BorderRadius.circular(32), border: Border.all(color: TemaApp.colorDorado.withValues(alpha: 0.58)), boxShadow: [ BoxShadow( - color: TemaApp.colorNaranja.withValues(alpha: 0.20), - blurRadius: 36, - offset: const Offset(0, 18), + color: TemaApp.colorNaranja.withValues(alpha: 0.25), + blurRadius: 44, + offset: const Offset(0, 22), ), BoxShadow( color: Colors.black.withValues(alpha: 0.50), - blurRadius: 22, - offset: const Offset(0, 10), + blurRadius: 28, + offset: const Offset(0, 14), ), ], ), foregroundDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(28), + borderRadius: BorderRadius.circular(32), gradient: LinearGradient( colors: [ Colors.white.withValues(alpha: 0.06), @@ -463,9 +557,14 @@ class _DeltaFuego extends StatelessWidget { Widget build(BuildContext context) { final texto = valor >= 0 ? '+$valor' : '$valor'; return Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), decoration: BoxDecoration( - color: TemaApp.colorNaranja.withValues(alpha: 0.18), + gradient: LinearGradient( + colors: [ + TemaApp.colorNaranja.withValues(alpha: 0.28), + TemaApp.colorDorado.withValues(alpha: 0.14), + ], + ), borderRadius: BorderRadius.circular(999), border: Border.all(color: TemaApp.colorNaranja), boxShadow: [ @@ -479,11 +578,11 @@ class _DeltaFuego extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.local_fire_department, - color: TemaApp.colorNaranja, size: 18), + color: TemaApp.colorNaranja, size: 24), const SizedBox(width: 4), Text( texto, - style: Theme.of(context).textTheme.titleMedium?.copyWith( + style: Theme.of(context).textTheme.headlineSmall?.copyWith( color: TemaApp.colorDorado, fontWeight: FontWeight.w900, ), @@ -529,9 +628,9 @@ class _BarraFuegoPremium extends StatelessWidget { ), ], ), - const SizedBox(height: 8), + const SizedBox(height: 12), Container( - height: 30, + height: 38, decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.72), borderRadius: BorderRadius.circular(999), @@ -579,6 +678,23 @@ class _BarraFuegoPremium extends StatelessWidget { ), ), ), + Positioned( + left: 12, + right: 12, + top: 5, + child: Container( + height: 6, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(999), + gradient: LinearGradient( + colors: [ + Colors.white.withValues(alpha: 0.62), + Colors.transparent, + ], + ), + ), + ), + ), ], ), ), @@ -714,12 +830,22 @@ class _TarjetaImpostores extends StatelessWidget { return _PanelRecompensa( child: Column( children: [ - Text( - '🎭 $titulo', - style: Theme.of(context).textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.w900, - color: Colors.white, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.theater_comedy, color: TemaApp.colorAcento), + const SizedBox(width: 8), + Flexible( + child: Text( + titulo, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w900, + color: Colors.white, + ), ), + ), + ], ), const SizedBox(height: 10), ...impostores.map(