Cambios visuales completos

This commit is contained in:
2026-05-10 17:28:35 +02:00
parent 8b4ca132aa
commit 42f01949c4
33 changed files with 887 additions and 397 deletions

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:provider/provider.dart';
import '../estado/estado_juego.dart';
import '../tema/componentes_farolero.dart';
import '../tema/tema_app.dart';
import 'pantalla_debate.dart';
import 'pantalla_fin_partida.dart';
@@ -41,24 +42,24 @@ class _PantallaAdivinanzaState extends State<PantallaAdivinanza> {
title: Text(l10n.impostorGuessTitle),
automaticallyImplyLeading: false,
),
body: Center(
child: SingleChildScrollView(
body: FondoFarolero(
intenso: true,
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('🎭', style: TextStyle(fontSize: 64)),
const SizedBox(height: 16),
Text(
l10n.impostorCanGuess,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
l10n.ifCorrectImpostorsWin,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: TemaApp.colorNaranja,
EncabezadoFarolero(
icono: Icons.theater_comedy,
titulo: l10n.impostorCanGuess,
subtitulo: l10n.ifCorrectImpostorsWin,
color: TemaApp.colorAcento,
trailing: Image.asset(
'assets/ui/premium/vote_danger_glow.png',
width: 42,
height: 42,
opacity: const AlwaysStoppedAnimation(0.64),
),
),
const SizedBox(height: 32),
@@ -231,6 +232,7 @@ class _PantallaAdivinanzaState extends State<PantallaAdivinanza> {
],
],
),
),
),
),
);

View File

@@ -23,6 +23,7 @@ class _PantallaAjustesState extends State<PantallaAjustes> {
return Scaffold(
appBar: AppBar(title: Text(l10n.settingsTitle)),
body: FondoFarolero(
intenso: true,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(

View File

@@ -317,42 +317,23 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
return Scaffold(
appBar: AppBar(title: Text(l10n.createGame)),
body: FondoFarolero(
intenso: true,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.fromLTRB(18, 18, 18, 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PanelFarolero(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
child: Row(
children: [
const Icon(Icons.groups, color: TemaApp.colorNaranja, size: 42),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'¿Cómo quieres jugar?',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 3),
Text(
l10n.playersRange,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
],
),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
EncabezadoFarolero(
icono: Icons.groups,
titulo: '¿Cómo quieres jugar?',
subtitulo: l10n.playersRange,
),
const SizedBox(height: 12),
if (!widget.bloquearModo) ...[
// Modo de juego
Card(
child: Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.fromLTRB(18, 18, 18, 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -416,7 +397,7 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
// Categoría
Card(
child: Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.fromLTRB(18, 18, 18, 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -453,7 +434,7 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
// Jugadores
Card(
child: Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.fromLTRB(18, 18, 18, 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -542,7 +523,7 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
// Configuración de partida
Card(
child: Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.fromLTRB(18, 18, 18, 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -614,22 +595,12 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
const SizedBox(height: 24),
// Botón iniciar
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton.icon(
onPressed: (_modoMultimovil || _jugadores.length >= 3)
? _iniciarPartida
: null,
icon: const Icon(Icons.play_arrow),
label: Text(l10n.startGame),
style: ElevatedButton.styleFrom(
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
BotonFarolero(
texto: l10n.startGame,
icono: Icons.play_arrow,
onPressed: (_modoMultimovil || _jugadores.length >= 3)
? _iniciarPartida
: null,
),
const SizedBox(height: 16),
],

View File

@@ -77,6 +77,7 @@ class _PantallaDebateState extends State<PantallaDebate> {
automaticallyImplyLeading: false,
),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
@@ -95,40 +96,56 @@ class _PantallaDebateState extends State<PantallaDebate> {
? Border.all(color: TemaApp.colorAcento, width: 2)
: null,
),
child: Column(
child: Stack(
alignment: Alignment.center,
children: [
Text(
_tiempoAgotado ? l10n.timeUp : l10n.timeRemaining,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: _tiempoAgotado
? TemaApp.colorAcento
: TemaApp.colorTextoSecundario,
Positioned.fill(
child: Image.asset(
'assets/ui/premium/timer_ring_glow.png',
fit: BoxFit.contain,
opacity: const AlwaysStoppedAnimation(0.36),
),
),
const SizedBox(height: 8),
Text(
_formatearTiempo(_segundosRestantes),
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontSize: 48,
fontWeight: FontWeight.bold,
color: _segundosRestantes < 10 && !_tiempoAgotado
? TemaApp.colorAcento
: TemaApp.colorTexto,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progreso,
backgroundColor: TemaApp.colorSuperficie,
valueColor: AlwaysStoppedAnimation(
_segundosRestantes < 10
? TemaApp.colorAcento
: TemaApp.colorVerde,
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_tiempoAgotado ? l10n.timeUp : l10n.timeRemaining,
style:
Theme.of(context).textTheme.titleMedium?.copyWith(
color: _tiempoAgotado
? TemaApp.colorAcento
: TemaApp.colorTextoSecundario,
),
),
minHeight: 6,
),
const SizedBox(height: 8),
Text(
_formatearTiempo(_segundosRestantes),
style:
Theme.of(context).textTheme.headlineLarge?.copyWith(
fontSize: 48,
fontWeight: FontWeight.bold,
color: _segundosRestantes < 10 &&
!_tiempoAgotado
? TemaApp.colorAcento
: TemaApp.colorTexto,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progreso,
backgroundColor: TemaApp.colorSuperficie,
valueColor: AlwaysStoppedAnimation(
_segundosRestantes < 10
? TemaApp.colorAcento
: TemaApp.colorVerde,
),
minHeight: 6,
),
),
],
),
],
),

View File

@@ -7,6 +7,7 @@ import 'package:farolero/pantallas/pantalla_notas_online.dart';
import 'package:farolero/pantallas/pantalla_revision_palabra.dart';
import 'package:farolero/pantallas/pantalla_votacion_cliente.dart';
import 'package:farolero/servicios/servicio_nearby.dart';
import 'package:farolero/tema/componentes_farolero.dart';
import 'package:farolero/tema/tema_app.dart';
import 'package:provider/provider.dart';
@@ -157,9 +158,11 @@ class _PantallaDebateClienteState extends State<PantallaDebateCliente> {
),
],
),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
const Spacer(),
@@ -173,23 +176,37 @@ class _PantallaDebateClienteState extends State<PantallaDebateCliente> {
: TemaApp.colorTarjeta,
borderRadius: BorderRadius.circular(24),
),
child: Column(
child: Stack(
alignment: Alignment.center,
children: [
Text(
_segundosRestantes == 0
? l10n.timeUp
: l10n.timeRemaining,
style: Theme.of(context).textTheme.titleMedium,
Positioned.fill(
child: Image.asset(
'assets/ui/premium/timer_ring_glow.png',
fit: BoxFit.contain,
opacity: const AlwaysStoppedAnimation(0.42),
),
),
const SizedBox(height: 8),
Text(
_formatearTiempo(_segundosRestantes),
style: Theme.of(context).textTheme.displayMedium?.copyWith(
fontWeight: FontWeight.bold,
color: _segundosRestantes == 0
? TemaApp.colorAcento
: TemaApp.colorTexto,
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_segundosRestantes == 0
? l10n.timeUp
: l10n.timeRemaining,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
_formatearTiempo(_segundosRestantes),
style:
Theme.of(context).textTheme.displayMedium?.copyWith(
fontWeight: FontWeight.bold,
color: _segundosRestantes == 0
? TemaApp.colorAcento
: TemaApp.colorTexto,
),
),
],
),
],
),
@@ -273,6 +290,7 @@ class _PantallaDebateClienteState extends State<PantallaDebateCliente> {
),
),
],
),
),
),
);

View File

@@ -96,9 +96,11 @@ class _PantallaFinPartidaOnlineState extends State<PantallaFinPartidaOnline> {
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
body: FondoFarolero(
intenso: true,
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Container(
width: double.infinity,
@@ -266,6 +268,7 @@ class _PantallaFinPartidaOnlineState extends State<PantallaFinPartidaOnline> {
),
),
],
),
),
),
);

View File

@@ -201,6 +201,7 @@ class _PantallaGestorHostState extends State<PantallaGestorHost> {
],
),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(

View File

@@ -16,6 +16,7 @@ class PantallaHistorial extends StatelessWidget {
return Scaffold(
appBar: AppBar(title: const Text('Historial')),
body: FondoFarolero(
intenso: true,
child: partidas.isEmpty
? const Center(child: Text('Todavía no hay partidas guardadas.'))
: ListView.builder(

View File

@@ -48,25 +48,66 @@ class _PantallaLobbyHostState extends State<PantallaLobbyHost> {
),
),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Container(
children: [
EncabezadoFarolero(
icono: Icons.wifi_tethering,
titulo: widget.nombreSala,
subtitulo: l10n.scanToJoin,
),
const SizedBox(height: 14),
PanelFarolero(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: QrImageView(
data: nearby.generarDatosQR(widget.nombreSala),
version: QrVersions.auto,
size: 160,
backgroundColor: Colors.white,
child: Column(
children: [
SizedBox(
width: 196,
height: 196,
child: Stack(
alignment: Alignment.center,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: TemaApp.colorNaranja.withValues(alpha: 0.18),
blurRadius: 24,
),
],
),
child: QrImageView(
data: nearby.generarDatosQR(widget.nombreSala),
version: QrVersions.auto,
size: 156,
backgroundColor: Colors.white,
),
),
Positioned.fill(
child: IgnorePointer(
child: Image.asset(
'assets/ui/premium/qr_frame_overlay.png',
fit: BoxFit.cover,
),
),
),
],
),
),
const SizedBox(height: 10),
Text(
'Escanea este código desde otro móvil',
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
],
),
),
const SizedBox(height: 12),
Text(l10n.scanToJoin),
const SizedBox(height: 16),
_buildResumenSala(context, seleccionados, nearby.jugadores.length),
const SizedBox(height: 12),
@@ -119,15 +160,15 @@ class _PantallaLobbyHostState extends State<PantallaLobbyHost> {
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
child: BotonFarolero(
texto: _iniciando ? l10n.starting : l10n.startGame,
icono: Icons.play_arrow,
onPressed: puedeIniciar && !_iniciando
? () {
setState(() => _iniciando = true);
widget.onIniciar();
}
: null,
icon: const Icon(Icons.play_arrow),
label: Text(_iniciando ? l10n.starting : l10n.startGame),
),
),
],

View File

@@ -3,6 +3,7 @@ import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:provider/provider.dart';
import '../estado/estado_juego.dart';
import '../servicios/servicio_notas.dart';
import '../tema/componentes_farolero.dart';
import '../tema/tema_app.dart';
class PantallaNotas extends StatefulWidget {
@@ -86,9 +87,12 @@ class _PantallaNotasState extends State<PantallaNotas> {
),
],
),
body: _jugadorSeleccionadoId == null
? _construirSelectorJugador(partida)
: _construirNotas(partida),
body: FondoFarolero(
intenso: true,
child: _jugadorSeleccionadoId == null
? _construirSelectorJugador(partida)
: _construirNotas(partida),
),
);
}

View File

@@ -3,6 +3,7 @@ import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:farolero/modelos/inicio_partida_multijugador.dart';
import 'package:farolero/modelos/jugador.dart';
import 'package:farolero/servicios/servicio_notas.dart';
import 'package:farolero/tema/componentes_farolero.dart';
import 'package:farolero/tema/tema_app.dart';
class PantallaNotasOnline extends StatefulWidget {
@@ -115,7 +116,10 @@ class _PantallaNotasOnlineState extends State<PantallaNotasOnline> {
),
],
),
body: _autor == null ? _buildSelector(context) : _buildNotas(context),
body: FondoFarolero(
intenso: true,
child: _autor == null ? _buildSelector(context) : _buildNotas(context),
),
),
);
}

View File

@@ -25,6 +25,7 @@ class PantallaPrincipal extends StatelessWidget {
final gamificacion = servicioPerfil.resumenGamificacion;
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: const Color(0xFF05070D),
body: FondoFarolero(
intenso: true,

View File

@@ -13,6 +13,7 @@ class PantallaReglas extends StatelessWidget {
return Scaffold(
appBar: AppBar(title: Text(l10n.rulesTitle)),
body: FondoFarolero(
intenso: true,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(

View File

@@ -64,6 +64,7 @@ class _PantallaResultadoState extends State<PantallaResultado>
automaticallyImplyLeading: false,
),
body: FondoFarolero(
intenso: true,
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(32),

View File

@@ -4,6 +4,7 @@ import '../modelos/inicio_partida_multijugador.dart';
import '../modelos/partida.dart';
import '../modelos/snapshot_partida_online.dart';
import '../servicios/servicio_nearby.dart';
import '../tema/componentes_farolero.dart';
import '../tema/tema_app.dart';
import 'pantalla_debate_cliente.dart';
import 'pantalla_fin_partida_online.dart';
@@ -168,11 +169,14 @@ class _PantallaResultadoOnlineState extends State<PantallaResultadoOnline> {
elevation: 0,
actions: _acciones(context, l10n),
),
body: Padding(
padding: const EdgeInsets.all(24),
child: resultado == null
? _buildEsperaAdivinanza(context, l10n)
: _buildResultado(context, l10n, resultado),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(24),
child: resultado == null
? _buildEsperaAdivinanza(context, l10n)
: _buildResultado(context, l10n, resultado),
),
),
);
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import '../tema/componentes_farolero.dart';
import '../tema/tema_app.dart';
@@ -10,40 +11,27 @@ class PantallaSeleccionModoJuego extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Elegir modo de juego')),
extendBodyBehindAppBar: true,
appBar: AppBar(title: const Text('Elegir modo')),
body: FondoFarolero(
intenso: true,
child: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
padding: const EdgeInsets.fromLTRB(20, 24, 20, 28),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 460),
constraints: const BoxConstraints(maxWidth: 470),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Icon(
Icons.sports_esports,
size: 64,
color: TemaApp.colorNaranja,
),
const SizedBox(height: 16),
Text(
'¿Cómo querés jugar?',
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Elegí primero el tipo de partida para configurar solo lo necesario.',
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 28),
const SizedBox(height: 12),
const _ModoHero().animate().fadeIn(duration: 320.ms).slideY(begin: -0.12),
const SizedBox(height: 34),
_ModoCard(
icono: Icons.phone_android,
titulo: 'Partida en este dispositivo',
descripcion:
'Todos los jugadores usan este móvil. Acá se agregan los nombres manualmente.',
icono: Icons.phone_android_rounded,
titulo: 'Un móvil',
subtitulo: 'Partida en este dispositivo',
descripcion: 'Ideal para jugar todos juntos pasando el móvil. Configuración rápida y directa.',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
@@ -53,13 +41,13 @@ class PantallaSeleccionModoJuego extends StatelessWidget {
),
),
),
),
const SizedBox(height: 14),
).animate().fadeIn(delay: 120.ms).slideX(begin: -0.08),
const SizedBox(height: 16),
_ModoCard(
icono: Icons.devices,
titulo: 'Partida multidispositivo',
descripcion:
'Este móvil crea el servidor. Los usuarios se gestionan después en el lobby.',
icono: Icons.devices_rounded,
titulo: 'Multidispositivo',
subtitulo: 'Cada jugador en su móvil',
descripcion: 'Crea una sala premium, comparte QR y gestiona usuarios desde el lobby.',
destacado: true,
onTap: () => Navigator.push(
context,
@@ -70,7 +58,7 @@ class PantallaSeleccionModoJuego extends StatelessWidget {
),
),
),
),
).animate().fadeIn(delay: 200.ms).slideX(begin: 0.08),
],
),
),
@@ -82,9 +70,82 @@ class PantallaSeleccionModoJuego extends StatelessWidget {
}
}
class _ModoHero extends StatelessWidget {
const _ModoHero();
@override
Widget build(BuildContext context) {
return SizedBox(
height: 230,
child: Stack(
alignment: Alignment.center,
children: [
Positioned.fill(
child: Image.asset(
'assets/ui/premium/lantern_radial_glow.png',
fit: BoxFit.contain,
opacity: const AlwaysStoppedAnimation(0.58),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 90,
height: 90,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
TemaApp.colorDorado.withValues(alpha: 0.95),
TemaApp.colorNaranja.withValues(alpha: 0.58),
Colors.black.withValues(alpha: 0.76),
],
),
border: Border.all(color: TemaApp.colorDorado, width: 3),
boxShadow: [
BoxShadow(
color: TemaApp.colorNaranja.withValues(alpha: 0.55),
blurRadius: 42,
spreadRadius: 5,
),
],
),
child: const Icon(Icons.sports_esports_rounded, size: 48, color: Color(0xFF241103)),
),
const SizedBox(height: 18),
Text(
'¿Cómo querés jugar?',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: TemaApp.colorDorado,
fontSize: 32,
fontWeight: FontWeight.w900,
shadows: [
Shadow(color: TemaApp.colorNaranja.withValues(alpha: 0.45), blurRadius: 16),
],
),
),
const SizedBox(height: 8),
Text(
'Elegí el tipo de partida y arrancá sin fricción.',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: TemaApp.colorTextoSecundario,
),
),
],
),
],
),
);
}
}
class _ModoCard extends StatelessWidget {
final IconData icono;
final String titulo;
final String subtitulo;
final String descripcion;
final bool destacado;
final VoidCallback onTap;
@@ -92,6 +153,7 @@ class _ModoCard extends StatelessWidget {
const _ModoCard({
required this.icono,
required this.titulo,
required this.subtitulo,
required this.descripcion,
required this.onTap,
this.destacado = false,
@@ -100,45 +162,86 @@ class _ModoCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final color = destacado ? TemaApp.colorNaranja : TemaApp.colorAcento;
return Card(
color: TemaApp.colorTarjeta,
return Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16),
borderRadius: BorderRadius.circular(28),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(18),
child: Row(
children: [
Container(
width: 52,
height: 52,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.18),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: color.withValues(alpha: 0.7)),
),
child: Icon(icono, color: color, size: 30),
child: Ink(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFF111C29).withValues(alpha: 0.94),
(destacado ? const Color(0xFF2A1620) : const Color(0xFF15111F)).withValues(alpha: 0.92),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(28),
border: Border.all(color: color.withValues(alpha: destacado ? 0.78 : 0.48)),
boxShadow: [
BoxShadow(
color: color.withValues(alpha: destacado ? 0.26 : 0.14),
blurRadius: destacado ? 34 : 22,
offset: const Offset(0, 14),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
],
),
child: Stack(
children: [
Positioned.fill(
child: Image.asset(
'assets/ui/premium/card_sheen_overlay.png',
fit: BoxFit.cover,
opacity: AlwaysStoppedAnimation(destacado ? 0.34 : 0.22),
),
),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Text(titulo, style: Theme.of(context).textTheme.titleLarge),
const SizedBox(height: 6),
Text(
descripcion,
style: Theme.of(context).textTheme.bodyMedium,
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.18),
borderRadius: BorderRadius.circular(22),
border: Border.all(color: color.withValues(alpha: 0.72)),
),
child: Icon(icono, color: color, size: 34),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
titulo.toUpperCase(),
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: destacado ? TemaApp.colorDorado : Colors.white,
fontWeight: FontWeight.w900,
letterSpacing: 0.8,
),
),
const SizedBox(height: 3),
Text(
subtitulo,
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: color),
),
const SizedBox(height: 7),
Text(descripcion, style: Theme.of(context).textTheme.bodyMedium),
],
),
),
const SizedBox(width: 8),
Icon(Icons.chevron_right_rounded, color: color, size: 32),
],
),
),
const SizedBox(width: 8),
const Icon(Icons.chevron_right),
],
),
),
),
);
}
}
}

View File

@@ -443,28 +443,19 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
return Scaffold(
appBar: AppBar(title: Text(l10n.joinGameTitle)),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(32),
child: Form(
key: _formKey,
child: Column(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.bluetooth_searching,
EncabezadoFarolero(
icono: Icons.bluetooth_searching,
titulo: l10n.joinGameTitle,
subtitulo: l10n.enterNameToSearch,
color: TemaApp.colorAzul,
size: 70,
),
const SizedBox(height: 24),
Text(
l10n.joinGameTitle,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(
l10n.enterNameToSearch,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
TextFormField(
@@ -481,13 +472,10 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
onFieldSubmitted: (_) => _iniciarBusqueda(),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _iniciarBusqueda,
icon: const Icon(Icons.search),
label: Text(l10n.searchGames),
),
BotonFarolero(
texto: l10n.searchGames,
icono: Icons.search,
onPressed: _iniciarBusqueda,
),
if (_error != null) ...[
const SizedBox(height: 16),
@@ -525,63 +513,41 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
),
),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
// Estado
if (_conectando) ...[
const CircularProgressIndicator(color: TemaApp.colorAcento),
const SizedBox(height: 12),
Text(
'${l10n.connectingTo} ${_salaSeleccionada ?? ""}...',
style: Theme.of(context).textTheme.bodyLarge,
children: [
EncabezadoFarolero(
icono: _conectando ? Icons.sync : Icons.radar,
titulo: _conectando
? '${l10n.connectingTo} ${_salaSeleccionada ?? ""}...'
: l10n.searchingGames,
subtitulo: _conectando
? 'Preparando la sala segura'
: 'Buscando partidas cercanas por Bluetooth',
color: _conectando ? TemaApp.colorAcento : TemaApp.colorNaranja,
trailing: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.4,
color: _conectando
? TemaApp.colorAcento
: TemaApp.colorNaranja,
),
),
const SizedBox(height: 24),
] else ...[
// Buscando
Row(
children: [
const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: TemaApp.colorNaranja,
),
),
const SizedBox(width: 12),
Text(
l10n.searchingGames,
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 24),
],
),
const SizedBox(height: 18),
// Lista de hosts encontrados
Expanded(
child: hosts.isEmpty && !_conectando
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('📡', style: TextStyle(fontSize: 48)),
const SizedBox(height: 16),
Text(
l10n.noGamesFound,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
l10n.noGamesFoundHint,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: Colors.grey),
textAlign: TextAlign.center,
),
],
child: EstadoVacioFarolero(
icono: Icons.radar,
titulo: l10n.noGamesFound,
subtitulo: l10n.noGamesFoundHint,
),
)
: ListView.builder(
@@ -627,42 +593,50 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
Widget _buildHostTile(String endpointId, String nombre) {
return Container(
margin: const EdgeInsets.only(bottom: 8),
margin: const EdgeInsets.only(bottom: 10),
child: Material(
color: TemaApp.colorTarjeta,
borderRadius: BorderRadius.circular(12),
color: Colors.transparent,
borderRadius: BorderRadius.circular(18),
child: InkWell(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(18),
onTap: _conectando ? null : () => _conectarAHost(endpointId, nombre),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Row(
children: [
const Text('🎭', style: TextStyle(fontSize: 28)),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
nombre,
style: Theme.of(context).textTheme.titleMedium,
),
Text(
'Toca para unirte',
style: Theme.of(
context,
).textTheme.bodySmall?.copyWith(color: Colors.grey),
),
],
child: Ink(
decoration: TemaApp.decoracionPanel(
color: TemaApp.colorTarjeta.withValues(alpha: 0.90),
borderColor: TemaApp.colorNaranja.withValues(alpha: 0.42),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Row(
children: [
const Icon(
Icons.theater_comedy,
color: TemaApp.colorNaranja,
size: 30,
),
),
const Icon(
Icons.arrow_forward_ios,
size: 16,
color: Colors.grey,
),
],
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
nombre,
style: Theme.of(context).textTheme.titleMedium,
),
Text(
'Toca para unirte',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
const Icon(
Icons.arrow_forward_ios,
size: 16,
color: TemaApp.colorDorado,
),
],
),
),
),
),
@@ -738,26 +712,26 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
),
),
body: FondoFarolero(
intenso: true,
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Estado de conexión
const Text('', style: TextStyle(fontSize: 64)),
const SizedBox(height: 24),
Text(
l10n.connectedWaiting,
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
EncabezadoFarolero(
icono: Icons.check_circle,
titulo: l10n.connectedWaiting,
subtitulo: '${l10n.yourName}: ${_nombreController.text}',
color: TemaApp.colorVerde,
trailing: const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.4,
color: TemaApp.colorNaranja,
),
),
),
const SizedBox(height: 12),
Text(
'${l10n.yourName}: ${_nombreController.text}',
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 32),
const CircularProgressIndicator(color: TemaApp.colorNaranja),
const SizedBox(height: 16),
Text(
l10n.waitingForHost,

View File

@@ -33,21 +33,15 @@ class _PantallaVerPalabraState extends State<PantallaVerPalabra> {
automaticallyImplyLeading: false,
),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
l10n.eachPlayerMustSee,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
l10n.roundNumber(partida.rondaActual),
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: TemaApp.colorNaranja,
),
EncabezadoFarolero(
icono: Icons.visibility,
titulo: l10n.roundNumber(partida.rondaActual),
subtitulo: l10n.eachPlayerMustSee,
),
const SizedBox(height: 16),
Expanded(

View File

@@ -60,6 +60,7 @@ class _PantallaVotacionState extends State<PantallaVotacion> {
automaticallyImplyLeading: false,
),
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
@@ -68,9 +69,9 @@ class _PantallaVotacionState extends State<PantallaVotacion> {
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: TemaApp.colorTarjeta,
borderRadius: BorderRadius.circular(12),
decoration: TemaApp.decoracionPanel(
color: TemaApp.colorTarjeta.withValues(alpha: 0.90),
borderColor: TemaApp.colorNaranja.withValues(alpha: 0.38),
),
child: Column(
children: [
@@ -105,9 +106,17 @@ class _PantallaVotacionState extends State<PantallaVotacion> {
),
const SizedBox(height: 16),
Text(
l10n.whoIsImpostor,
style: Theme.of(context).textTheme.titleMedium,
EncabezadoFarolero(
icono: Icons.how_to_vote,
titulo: l10n.whoIsImpostor,
subtitulo: l10n.selectOnePlayer,
color: TemaApp.colorAcento,
trailing: Image.asset(
'assets/ui/premium/vote_danger_glow.png',
width: 42,
height: 42,
opacity: const AlwaysStoppedAnimation(0.64),
),
),
const SizedBox(height: 12),
@@ -178,6 +187,7 @@ class _PantallaVotacionState extends State<PantallaVotacion> {
automaticallyImplyLeading: false,
),
body: FondoFarolero(
intenso: true,
child: Center(
child: Padding(
padding: const EdgeInsets.all(32),

View File

@@ -7,6 +7,7 @@ import 'package:farolero/pantallas/pantalla_notas_online.dart';
import 'package:farolero/pantallas/pantalla_revision_palabra.dart';
import 'package:farolero/pantallas/pantalla_resultado_online.dart';
import 'package:farolero/servicios/servicio_nearby.dart';
import 'package:farolero/tema/componentes_farolero.dart';
import 'package:farolero/tema/tema_app.dart';
import 'package:provider/provider.dart';
@@ -126,21 +127,26 @@ class _PantallaVotacionClienteState extends State<PantallaVotacionCliente> {
),
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.whoDoYouThinkIsTheImpostor,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
Text(
_modoMultiVotante
EncabezadoFarolero(
icono: Icons.how_to_vote,
titulo: l10n.whoDoYouThinkIsTheImpostor,
subtitulo: _modoMultiVotante
? 'Emití un voto por cada jugador que manejás.'
: l10n.selectOnePlayer,
style: TextStyle(color: TemaApp.colorTextoSecundario),
color: TemaApp.colorAcento,
trailing: Image.asset(
'assets/ui/premium/vote_danger_glow.png',
width: 42,
height: 42,
opacity: const AlwaysStoppedAnimation(0.64),
),
),
const SizedBox(height: 16),
Expanded(
@@ -174,6 +180,7 @@ class _PantallaVotacionClienteState extends State<PantallaVotacionCliente> {
],
),
),
),
);
}
@@ -240,9 +247,11 @@ class _PantallaVotacionClienteState extends State<PantallaVotacionCliente> {
),
],
),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
body: FondoFarolero(
intenso: true,
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
@@ -339,6 +348,7 @@ class _PantallaVotacionClienteState extends State<PantallaVotacionCliente> {
),
),
],
),
),
),
);

View File

@@ -20,9 +20,36 @@ class FondoFarolero extends StatelessWidget {
Widget build(BuildContext context) {
return DecoratedBox(
decoration: const BoxDecoration(gradient: TemaApp.gradienteFondo),
child: CustomPaint(
painter: _FondoFaroleroPainter(intenso: intenso),
child: child,
child: Stack(
children: [
Positioned.fill(
child: CustomPaint(painter: _FondoFaroleroPainter(intenso: intenso)),
),
Positioned(
top: intenso ? -180 : -140,
left: -220,
right: -220,
child: IgnorePointer(
child: Image.asset(
'assets/ui/premium/lantern_radial_glow.png',
height: intenso ? 720 : 560,
fit: BoxFit.contain,
opacity: AlwaysStoppedAnimation(intenso ? 0.56 : 0.34),
),
),
),
Positioned.fill(
child: IgnorePointer(
child: Image.asset(
'assets/ui/premium/sparks_overlay.png',
fit: BoxFit.cover,
repeat: ImageRepeat.repeat,
opacity: AlwaysStoppedAnimation(intenso ? 0.38 : 0.22),
),
),
),
Positioned.fill(child: child),
],
),
);
}
@@ -49,9 +76,138 @@ class PanelFarolero extends StatelessWidget {
return Container(
width: double.infinity,
margin: margin,
padding: padding,
decoration: TemaApp.decoracionPanel(color: color, borderColor: borderColor),
child: child,
child: ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Stack(
children: [
Positioned.fill(
child: Image.asset(
'assets/ui/premium/card_sheen_overlay.png',
fit: BoxFit.cover,
opacity: const AlwaysStoppedAnimation(0.26),
),
),
Padding(padding: padding, child: child),
],
),
),
);
}
}
class EncabezadoFarolero extends StatelessWidget {
final IconData icono;
final String titulo;
final String? subtitulo;
final Color color;
final Widget? trailing;
final EdgeInsetsGeometry padding;
const EncabezadoFarolero({
super.key,
required this.icono,
required this.titulo,
this.subtitulo,
this.color = TemaApp.colorNaranja,
this.trailing,
this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
});
@override
Widget build(BuildContext context) {
return PanelFarolero(
padding: padding,
child: Row(
children: [
Container(
width: 52,
height: 52,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
color.withValues(alpha: 0.34),
TemaApp.colorSuperficie.withValues(alpha: 0.72),
],
),
border: Border.all(color: color.withValues(alpha: 0.72)),
boxShadow: [
BoxShadow(
color: color.withValues(alpha: 0.22),
blurRadius: 22,
),
],
),
child: Icon(icono, color: color, size: 30),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
titulo,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: TemaApp.colorDorado,
fontWeight: FontWeight.w900,
),
),
if (subtitulo != null) ...[
const SizedBox(height: 3),
Text(
subtitulo!,
style: Theme.of(context).textTheme.bodyMedium,
),
],
],
),
),
if (trailing != null) ...[
const SizedBox(width: 12),
trailing!,
],
],
),
);
}
}
class EstadoVacioFarolero extends StatelessWidget {
final IconData icono;
final String titulo;
final String subtitulo;
const EstadoVacioFarolero({
super.key,
required this.icono,
required this.titulo,
required this.subtitulo,
});
@override
Widget build(BuildContext context) {
return PanelFarolero(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 28),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icono, color: TemaApp.colorNaranja, size: 46),
const SizedBox(height: 14),
Text(
titulo,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
subtitulo,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
],
),
);
}
}
@@ -138,17 +294,17 @@ class BotonFarolero extends StatelessWidget {
return Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(18),
onTap: onPressed,
child: Ink(
height: 54,
height: 58,
decoration: BoxDecoration(
gradient: habilitado
? gradient
: const LinearGradient(
colors: [TemaApp.colorTarjeta, TemaApp.colorSuperficie],
),
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: habilitado
? TemaApp.colorDorado.withValues(alpha: 0.74)
@@ -157,29 +313,46 @@ class BotonFarolero extends StatelessWidget {
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.34),
blurRadius: 14,
offset: const Offset(0, 8),
blurRadius: 18,
offset: const Offset(0, 10),
),
if (habilitado)
BoxShadow(
color: TemaApp.colorNaranja.withValues(alpha: 0.16),
blurRadius: 22,
),
],
),
child: Row(
child: Stack(
children: [
SizedBox(
width: 58,
child: Icon(icono, color: foreground, size: 28),
),
Expanded(
child: Text(
texto.toUpperCase(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: foreground,
fontSize: 18,
fontWeight: FontWeight.w800,
),
Positioned.fill(
child: Image.asset(
'assets/ui/premium/card_sheen_overlay.png',
fit: BoxFit.cover,
opacity: const AlwaysStoppedAnimation(0.18),
),
),
const SizedBox(width: 58),
Row(
children: [
SizedBox(
width: 58,
child: Icon(icono, color: foreground, size: 28),
),
Expanded(
child: Text(
texto.toUpperCase(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: foreground,
fontSize: 18,
fontWeight: FontWeight.w900,
letterSpacing: 0.8,
),
),
),
const SizedBox(width: 58),
],
),
],
),
),
@@ -254,15 +427,27 @@ class TarjetaPalabraFarolero extends StatelessWidget {
),
],
),
child: Text(
palabra.toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.oswald(
color: const Color(0xFF1B0C05),
fontSize: 42,
fontWeight: FontWeight.w900,
letterSpacing: 0,
),
child: Stack(
alignment: Alignment.center,
children: [
Positioned.fill(
child: Image.asset(
'assets/ui/premium/word_reveal_glow.png',
fit: BoxFit.cover,
opacity: const AlwaysStoppedAnimation(0.28),
),
),
Text(
palabra.toUpperCase(),
textAlign: TextAlign.center,
style: GoogleFonts.oswald(
color: const Color(0xFF1B0C05),
fontSize: 42,
fontWeight: FontWeight.w900,
letterSpacing: 0,
),
),
],
),
);
}

View File

@@ -89,8 +89,8 @@ class TemaApp {
elevation: 0,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: colorBorde),
borderRadius: BorderRadius.circular(18),
side: BorderSide(color: colorDorado.withValues(alpha: 0.34)),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
@@ -99,12 +99,13 @@ class TemaApp {
foregroundColor: Colors.black,
disabledBackgroundColor: colorTarjeta,
disabledForegroundColor: colorTextoSecundario,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
elevation: 0,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
textStyle: GoogleFonts.oswald(
fontWeight: FontWeight.w700,
fontSize: 16,
letterSpacing: 0,
fontSize: 17,
letterSpacing: 0.6,
),
),
),
@@ -112,8 +113,8 @@ class TemaApp {
style: OutlinedButton.styleFrom(
foregroundColor: colorTexto,
side: const BorderSide(color: colorBorde),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
textStyle: GoogleFonts.oswald(
fontWeight: FontWeight.w700,
fontSize: 16,
@@ -125,22 +126,22 @@ class TemaApp {
filled: true,
fillColor: const Color(0xFF0B1117),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(16),
borderSide: const BorderSide(color: colorBorde),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(16),
borderSide: const BorderSide(color: colorBorde),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(16),
borderSide: const BorderSide(color: colorNaranja),
),
labelStyle: const TextStyle(color: colorTextoSecundario),
hintStyle: const TextStyle(color: colorTextoSecundario),
),
appBarTheme: AppBarTheme(
backgroundColor: colorFondo,
backgroundColor: Colors.transparent,
foregroundColor: colorNaranja,
centerTitle: true,
elevation: 0,
@@ -199,13 +200,19 @@ class TemaApp {
}) {
return BoxDecoration(
color: color ?? colorTarjeta.withValues(alpha: 0.84),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: borderColor ?? colorBorde),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: borderColor ?? colorDorado.withValues(alpha: 0.30),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.35),
blurRadius: 18,
offset: const Offset(0, 10),
blurRadius: 22,
offset: const Offset(0, 12),
),
BoxShadow(
color: colorNaranja.withValues(alpha: 0.10),
blurRadius: 24,
),
],
);