import 'dart:math' as math; import 'package:farolero/l10n/generated/app_localizations.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:provider/provider.dart'; import '../servicios/servicio_perfil_usuario.dart'; import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_ajustes.dart'; import 'pantalla_historial.dart'; import 'pantalla_reglas.dart'; import 'pantalla_seleccion_modo_juego.dart'; import 'pantalla_unirse.dart'; class PantallaPrincipal extends StatelessWidget { const PantallaPrincipal({super.key}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final servicioPerfil = context.watch(); final perfil = servicioPerfil.perfil; final gamificacion = servicioPerfil.resumenGamificacion; return Scaffold( backgroundColor: const Color(0xFF05070D), body: FondoFarolero( intenso: true, child: Stack( children: [ const Positioned.fill( child: IgnorePointer(child: CustomPaint(painter: _InicioFondoPainter())), ), Positioned.fill( child: IgnorePointer( child: DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( colors: [ Colors.black.withValues(alpha: 0.05), Colors.transparent, Colors.black.withValues(alpha: 0.58), ], stops: const [0.0, 0.48, 1.0], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), ), ), SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB(20, 18, 20, 24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 430), child: Column( children: [ _PerfilInicioPremium( nombre: perfil.nombre, nick: perfil.nick, avatarAsset: perfil.avatarAsset, fuego: gamificacion.fuego, medallas: gamificacion.medallas, onAjustes: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const PantallaAjustes(), ), ); }, ajustesTooltip: l10n.settings, ).animate().fadeIn(duration: 280.ms).slideY(begin: -0.12), const SizedBox(height: 42), _HeroInicioPremium(subtitulo: l10n.subtitle) .animate() .fadeIn(delay: 120.ms, duration: 420.ms) .scale(begin: const Offset(0.92, 0.92)), const SizedBox(height: 46), _BotonInicioPremium.primario( texto: 'Jugar', icono: Icons.play_arrow_rounded, onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const PantallaSeleccionModoJuego(), ), ); }, ).animate().fadeIn(delay: 240.ms).slideY(begin: 0.16), const SizedBox(height: 14), _BotonInicioPremium.secundario( texto: l10n.joinGame, icono: Icons.bolt_rounded, onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const PantallaUnirse()), ); }, ).animate().fadeIn(delay: 320.ms).slideY(begin: 0.16), const SizedBox(height: 12), _BotonInicioPremium.oscuro( texto: l10n.howToPlay, icono: Icons.question_mark_rounded, onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const PantallaReglas()), ); }, ).animate().fadeIn(delay: 390.ms).slideY(begin: 0.16), const SizedBox(height: 14), _AccesoHistorialPremium( etiqueta: 'Historial', onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const PantallaHistorial()), ); }, ).animate().fadeIn(delay: 470.ms).slideY(begin: 0.14), const SizedBox(height: 28), Text( l10n.playersRange, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: TemaApp.colorTextoSecundario.withValues(alpha: 0.82), letterSpacing: 0.8, ), ), ], ), ), ), ), ), ], ), ), ); } } class _PerfilInicioPremium extends StatelessWidget { final String nombre; final String nick; final String? avatarAsset; final int fuego; final List medallas; final VoidCallback onAjustes; final String ajustesTooltip; const _PerfilInicioPremium({ required this.nombre, required this.nick, required this.avatarAsset, required this.fuego, required this.medallas, required this.onAjustes, required this.ajustesTooltip, }); @override Widget build(BuildContext context) { final progreso = (fuego / 100).clamp(0.0, 1.0).toDouble(); return Container( padding: const EdgeInsets.fromLTRB(14, 12, 10, 12), decoration: _decoracionCristal(radius: 28, alpha: 0.82), child: Row( children: [ AvatarFarolero( texto: nombre.substring(0, 1).toUpperCase(), assetPath: avatarAsset, size: 58, fuego: fuego, medallas: medallas, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( nombre, maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w900, ), ), Text( '@$nick', maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: TemaApp.colorTextoSecundario, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 8), ClipRRect( borderRadius: BorderRadius.circular(999), child: Stack( children: [ Container(height: 8, color: Colors.black.withValues(alpha: 0.55)), FractionallySizedBox( widthFactor: progreso, child: Container( height: 8, decoration: const BoxDecoration( gradient: LinearGradient( colors: [ Color(0xFFE53935), TemaApp.colorNaranja, TemaApp.colorDorado, ], ), ), ), ), ], ), ), ], ), ), const SizedBox(width: 10), IconButton.filledTonal( tooltip: ajustesTooltip, onPressed: onAjustes, style: IconButton.styleFrom( backgroundColor: TemaApp.colorDorado.withValues(alpha: 0.14), foregroundColor: TemaApp.colorDorado, side: BorderSide(color: TemaApp.colorDorado.withValues(alpha: 0.44)), ), icon: const Icon(Icons.settings_rounded), ), ], ), ); } } class _HeroInicioPremium extends StatelessWidget { final String subtitulo; const _HeroInicioPremium({required this.subtitulo}); @override Widget build(BuildContext context) { return SizedBox( height: 230, child: Stack( alignment: Alignment.center, children: [ const Positioned.fill( child: IgnorePointer(child: CustomPaint(painter: _HeroInicioPainter())), ), Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 92, height: 92, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ TemaApp.colorDorado.withValues(alpha: 0.92), TemaApp.colorNaranja.withValues(alpha: 0.55), Colors.black.withValues(alpha: 0.78), ], ), border: Border.all(color: TemaApp.colorDorado, width: 3), boxShadow: [ BoxShadow( color: TemaApp.colorNaranja.withValues(alpha: 0.65), blurRadius: 42, spreadRadius: 7, ), ], ), child: const Icon( Icons.lightbulb_rounded, color: Color(0xFF251304), size: 54, ), ).animate(onPlay: (controller) => controller.repeat(reverse: true)).scale( begin: const Offset(0.98, 0.98), end: const Offset(1.04, 1.04), duration: 1400.ms, curve: Curves.easeInOut, ), const SizedBox(height: 12), const LogoFarolero(size: 64), const SizedBox(height: 8), Text( subtitulo, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: TemaApp.colorTexto, fontSize: 17, fontWeight: FontWeight.w800, shadows: [ Shadow( color: TemaApp.colorNaranja.withValues(alpha: 0.35), blurRadius: 14, ), ], ), ), const SizedBox(height: 4), Text( 'Descubr� al impostor antes de que sea tarde', textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: TemaApp.colorTextoSecundario, fontWeight: FontWeight.w600, ), ), ], ), ], ), ); } } class _BotonInicioPremium extends StatelessWidget { final String texto; final IconData icono; final VoidCallback onPressed; final LinearGradient gradient; final Color foreground; final double height; final bool hero; const _BotonInicioPremium._({ required this.texto, required this.icono, required this.onPressed, required this.gradient, required this.foreground, required this.height, required this.hero, }); factory _BotonInicioPremium.primario({ required String texto, required IconData icono, required VoidCallback onPressed, }) { return _BotonInicioPremium._( texto: texto, icono: icono, onPressed: onPressed, gradient: const LinearGradient( colors: [Color(0xFFFFE28A), TemaApp.colorDorado, TemaApp.colorNaranja], begin: Alignment.topLeft, end: Alignment.bottomRight, ), foreground: const Color(0xFF1C0D03), height: 72, hero: true, ); } factory _BotonInicioPremium.secundario({ required String texto, required IconData icono, required VoidCallback onPressed, }) { return _BotonInicioPremium._( texto: texto, icono: icono, onPressed: onPressed, gradient: const LinearGradient( colors: [Color(0xFF211730), Color(0xFF121B28)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), foreground: Colors.white, height: 60, hero: false, ); } factory _BotonInicioPremium.oscuro({ required String texto, required IconData icono, required VoidCallback onPressed, }) { return _BotonInicioPremium._( texto: texto, icono: icono, onPressed: onPressed, gradient: const LinearGradient( colors: [Color(0xFF101A24), Color(0xFF080D13)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), foreground: TemaApp.colorTexto, height: 60, hero: false, ); } @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(hero ? 26 : 22), onTap: onPressed, child: Ink( height: height, decoration: BoxDecoration( gradient: gradient, borderRadius: BorderRadius.circular(hero ? 26 : 22), border: Border.all( color: hero ? const Color(0xFFFFF0B8) : TemaApp.colorDorado.withValues(alpha: 0.34), ), boxShadow: [ BoxShadow( color: (hero ? TemaApp.colorNaranja : Colors.black).withValues(alpha: hero ? 0.46 : 0.42), blurRadius: hero ? 34 : 18, offset: const Offset(0, 12), ), ], ), child: Row( children: [ SizedBox(width: hero ? 70 : 62, child: Icon(icono, color: foreground, size: hero ? 38 : 27)), Expanded( child: Text( texto.toUpperCase(), textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge?.copyWith( color: foreground, fontSize: hero ? 28 : 18, fontWeight: FontWeight.w900, letterSpacing: hero ? 1.6 : 1.0, ), ), ), SizedBox(width: hero ? 70 : 62), ], ), ), ), ); } } class _AccesoHistorialPremium extends StatelessWidget { final String etiqueta; final VoidCallback onPressed; const _AccesoHistorialPremium({required this.etiqueta, required this.onPressed}); @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(22), onTap: onPressed, child: Ink( height: 58, decoration: _decoracionCristal(radius: 22, alpha: 0.72), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.history_rounded, color: TemaApp.colorNaranja), const SizedBox(width: 10), Text( etiqueta.toUpperCase(), style: Theme.of(context).textTheme.titleMedium?.copyWith( color: TemaApp.colorDorado, fontWeight: FontWeight.w900, letterSpacing: 1.2, ), ), ], ), ), ), ); } } BoxDecoration _decoracionCristal({required double radius, required double alpha}) { return BoxDecoration( gradient: LinearGradient( colors: [ const Color(0xFF111C29).withValues(alpha: alpha), const Color(0xFF160C1F).withValues(alpha: alpha - 0.08), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(radius), border: Border.all(color: TemaApp.colorDorado.withValues(alpha: 0.42)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.46), blurRadius: 24, offset: const Offset(0, 12), ), BoxShadow( color: TemaApp.colorNaranja.withValues(alpha: 0.14), blurRadius: 26, ), ], ); } class _InicioFondoPainter extends CustomPainter { const _InicioFondoPainter(); @override void paint(Canvas canvas, Size size) { final paint = Paint()..isAntiAlias = true; paint.shader = const LinearGradient( colors: [Color(0xFF050A12), Color(0xFF0A1524), Color(0xFF15091D)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ).createShader(Offset.zero & size); canvas.drawRect(Offset.zero & size, paint); final farol = Offset(size.width * 0.5, size.height * 0.34); paint.shader = RadialGradient( colors: [ TemaApp.colorDorado.withValues(alpha: 0.28), TemaApp.colorNaranja.withValues(alpha: 0.12), Colors.transparent, ], ).createShader(Rect.fromCircle(center: farol, radius: size.width * 0.78)); canvas.drawCircle(farol, size.width * 0.78, paint); paint.shader = null; _drawSkyline(canvas, size, paint); _drawSparks(canvas, size); } void _drawSkyline(Canvas canvas, Size size, Paint paint) { paint.color = Colors.black.withValues(alpha: 0.38); final base = size.height * 0.80; final path = Path() ..moveTo(0, size.height) ..lineTo(0, base - 20) ..lineTo(size.width * 0.14, base - 58) ..lineTo(size.width * 0.26, base - 24) ..lineTo(size.width * 0.42, base - 78) ..lineTo(size.width * 0.58, base - 34) ..lineTo(size.width * 0.74, base - 74) ..lineTo(size.width * 0.90, base - 28) ..lineTo(size.width, base - 48) ..lineTo(size.width, size.height) ..close(); canvas.drawPath(path, paint); paint.color = TemaApp.colorNaranja.withValues(alpha: 0.18); canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromCenter(center: Offset(size.width * 0.5, base - 108), width: 24, height: 150), const Radius.circular(12), ), paint, ); } void _drawSparks(Canvas canvas, Size size) { final palette = [TemaApp.colorDorado, TemaApp.colorNaranja, const Color(0xFFFFF2C9)]; for (var i = 0; i < 64; i++) { final x = (i * 67 % math.max(size.width, 1)).toDouble(); final y = (i * 113 % math.max(size.height, 1)).toDouble(); final paint = Paint() ..isAntiAlias = true ..color = palette[i % palette.length].withValues(alpha: 0.12 + (i % 4) * 0.06); canvas.drawCircle(Offset(x, y), 1.2 + (i % 3), paint); } } @override bool shouldRepaint(covariant _InicioFondoPainter oldDelegate) => false; } class _HeroInicioPainter extends CustomPainter { const _HeroInicioPainter(); @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height * 0.45); final paint = Paint()..isAntiAlias = true; paint.shader = RadialGradient( colors: [TemaApp.colorNaranja.withValues(alpha: 0.30), Colors.transparent], ).createShader(Rect.fromCircle(center: center, radius: size.width * 0.54)); canvas.drawCircle(center, size.width * 0.54, paint); paint.shader = null; for (var i = 0; i < 24; i++) { final angle = math.pi * 2 * i / 24; paint.color = (i.isEven ? TemaApp.colorDorado : TemaApp.colorNaranja).withValues(alpha: i.isEven ? 0.14 : 0.08); canvas.drawPath( Path() ..moveTo(center.dx + math.cos(angle - 0.035) * 45, center.dy + math.sin(angle - 0.035) * 45) ..lineTo(center.dx + math.cos(angle) * size.width * 0.44, center.dy + math.sin(angle) * size.width * 0.44) ..lineTo(center.dx + math.cos(angle + 0.035) * 45, center.dy + math.sin(angle + 0.035) * 45) ..close(), paint, ); } } @override bool shouldRepaint(covariant _HeroInicioPainter oldDelegate) => false; }