diff --git a/docs/prototipos/propotipo inicial.png b/docs/prototipos/propotipo inicial.png new file mode 100644 index 0000000..bab3947 Binary files /dev/null and b/docs/prototipos/propotipo inicial.png differ diff --git a/lib/main.dart b/lib/main.dart index 7433f8e..376f6ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:provider/provider.dart'; import 'estado/estado_juego.dart'; import 'servicios/servicio_idioma.dart'; import 'servicios/servicio_nearby.dart'; +import 'tema/componentes_farolero.dart'; import 'tema/tema_app.dart'; import 'pantallas/pantalla_principal.dart'; @@ -71,24 +72,42 @@ class PantallaCarga extends StatelessWidget { if (estado.cargando || estado.banco == null) { return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('🎭', style: TextStyle(fontSize: 72)), - const SizedBox(height: 24), - Text( - l10n?.appTitle ?? 'Farolero', - style: Theme.of(context).textTheme.headlineLarge, + body: FondoFarolero( + intenso: true, + child: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 34, vertical: 28), + child: Column( + children: [ + const Spacer(flex: 2), + const Icon( + Icons.lightbulb, + color: TemaApp.colorNaranja, + size: 86, + ), + const SizedBox(height: 18), + const LogoFarolero(size: 58), + const Spacer(flex: 3), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: const LinearProgressIndicator( + minHeight: 8, + color: TemaApp.colorNaranja, + backgroundColor: TemaApp.colorSuperficie, + ), + ), + const SizedBox(height: 14), + Text( + l10n?.loadingWords ?? 'Cargando palabras...', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: TemaApp.colorDorado, + ), + textAlign: TextAlign.center, + ), + const Spacer(), + ], ), - const SizedBox(height: 16), - const CircularProgressIndicator(color: TemaApp.colorAcento), - const SizedBox(height: 12), - Text( - l10n?.loadingWords ?? 'Cargando palabras...', - style: Theme.of(context).textTheme.bodyMedium, - ), - ], + ), ), ), ); diff --git a/lib/pantallas/pantalla_ajustes.dart b/lib/pantallas/pantalla_ajustes.dart index ba9488b..e2a2dc6 100644 --- a/lib/pantallas/pantalla_ajustes.dart +++ b/lib/pantallas/pantalla_ajustes.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; import 'package:provider/provider.dart'; import '../servicios/servicio_idioma.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; class PantallaAjustes extends StatefulWidget { @@ -22,9 +23,10 @@ class _PantallaAjustesState extends State { return Scaffold( appBar: AppBar(title: Text(l10n.settingsTitle)), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Selector de idioma @@ -133,6 +135,7 @@ class _PantallaAjustesState extends State { ), const SizedBox(height: 16), ], + ), ), ), ); diff --git a/lib/pantallas/pantalla_crear_partida.dart b/lib/pantallas/pantalla_crear_partida.dart index bb6bd20..a8c34fd 100644 --- a/lib/pantallas/pantalla_crear_partida.dart +++ b/lib/pantallas/pantalla_crear_partida.dart @@ -8,6 +8,7 @@ import '../modelos/partida.dart'; import '../modelos/usuario.dart'; import '../servicios/servicio_nearby.dart'; import '../servicios/servicio_permisos.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_gestor_host.dart'; import 'pantalla_lobby_host.dart'; @@ -409,11 +410,38 @@ class _PantallaCrearPartidaState extends State { return Scaffold( appBar: AppBar(title: Text(l10n.createGame)), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + 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, + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 12), // Modo de juego Card( child: Padding( @@ -648,6 +676,7 @@ class _PantallaCrearPartidaState extends State { ), const SizedBox(height: 16), ], + ), ), ), ); diff --git a/lib/pantallas/pantalla_debate.dart b/lib/pantallas/pantalla_debate.dart index 275c987..5ca9503 100644 --- a/lib/pantallas/pantalla_debate.dart +++ b/lib/pantallas/pantalla_debate.dart @@ -3,6 +3,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_notas.dart'; import 'pantalla_votacion.dart'; @@ -75,9 +76,10 @@ class _PantallaDebateState extends State { title: Text(l10n.debateRound(partida.rondaActual)), automaticallyImplyLeading: false, ), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( children: [ // Temporizador if (tieneTemporizador) ...[ @@ -223,6 +225,7 @@ class _PantallaDebateState extends State { ], ), ], + ), ), ), ); diff --git a/lib/pantallas/pantalla_gestor_host.dart b/lib/pantallas/pantalla_gestor_host.dart index bba6d7b..4b6cdc0 100644 --- a/lib/pantallas/pantalla_gestor_host.dart +++ b/lib/pantallas/pantalla_gestor_host.dart @@ -6,6 +6,7 @@ import '../estado/estado_juego.dart'; import '../modelos/inicio_partida_multijugador.dart'; import '../modelos/partida.dart'; import '../servicios/servicio_nearby.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_votacion_cliente.dart'; import 'pantalla_palabras_cliente.dart'; @@ -139,9 +140,10 @@ class _PantallaGestorHostState extends State { ), ], ), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( children: [ _buildFaseIndicator(context, partida.fase, l10n), const SizedBox(height: 16), @@ -163,6 +165,7 @@ class _PantallaGestorHostState extends State { todosVotaron, ), ], + ), ), ), ); @@ -674,10 +677,12 @@ class _PantallaRevelarPalabraHostState return Scaffold( appBar: AppBar(title: Text(widget.nombre)), - body: Center( - child: Padding( - padding: const EdgeInsets.all(32), - child: Column( + body: FondoFarolero( + intenso: true, + child: Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( @@ -726,12 +731,7 @@ class _PantallaRevelarPalabraHostState ), if (!widget.esImpostor) ...[ const SizedBox(height: 12), - Text( - widget.palabra, - style: Theme.of(context).textTheme.headlineLarge - ?.copyWith(fontSize: 32, color: Colors.white), - textAlign: TextAlign.center, - ), + TarjetaPalabraFarolero(palabra: widget.palabra), ], if (widget.esImpostor && widget.pistaActiva) ...[ const SizedBox(height: 12), @@ -788,6 +788,7 @@ class _PantallaRevelarPalabraHostState ), ), ], + ), ), ), ), diff --git a/lib/pantallas/pantalla_lobby_host.dart b/lib/pantallas/pantalla_lobby_host.dart index cc5cbfb..d0e1ff7 100644 --- a/lib/pantallas/pantalla_lobby_host.dart +++ b/lib/pantallas/pantalla_lobby_host.dart @@ -4,6 +4,7 @@ import 'package:qr_flutter/qr_flutter.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; import '../modelos/usuario.dart'; import '../servicios/servicio_nearby.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; /// Lobby del host. El host es autoridad de sala y también cliente local. @@ -45,9 +46,10 @@ class _PantallaLobbyHostState extends State { }, ), ), - body: Padding( - padding: const EdgeInsets.all(24), - child: Column( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( children: [ Container( padding: const EdgeInsets.all(16), @@ -128,6 +130,7 @@ class _PantallaLobbyHostState extends State { ), ), ], + ), ), ), ); @@ -176,6 +179,7 @@ class _PantallaLobbyHostState extends State { decoration: BoxDecoration( color: color.withValues(alpha: 0.18), borderRadius: BorderRadius.circular(12), + border: Border.all(color: color.withValues(alpha: 0.55)), ), child: Row( children: [ @@ -202,12 +206,16 @@ class _PantallaLobbyHostState extends State { return ListTile( leading: CircleAvatar( - backgroundColor: seleccionadoPorMi - ? TemaApp.colorVerde - : seleccionadoPorOtro - ? TemaApp.colorNaranja - : TemaApp.colorTarjeta, - child: Text(usuario.avatar ?? '👤'), + backgroundColor: Colors.transparent, + child: AvatarFarolero( + texto: usuario.avatar ?? (usuario.nombre.isEmpty ? '?' : usuario.nombre[0]), + color: seleccionadoPorMi + ? TemaApp.colorVerde + : seleccionadoPorOtro + ? TemaApp.colorNaranja + : TemaApp.colorAzul, + size: 38, + ), ), title: Text(usuario.nombre), subtitle: Text( diff --git a/lib/pantallas/pantalla_palabra_cliente.dart b/lib/pantallas/pantalla_palabra_cliente.dart index e37df06..8b1bb08 100644 --- a/lib/pantallas/pantalla_palabra_cliente.dart +++ b/lib/pantallas/pantalla_palabra_cliente.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; +import 'package:farolero/tema/componentes_farolero.dart'; import 'package:farolero/tema/tema_app.dart'; /// Pantalla que ve cada jugador cuando recibe su palabra (modo multidispositivo). @@ -44,8 +45,9 @@ class _PantallaPalabraClienteState extends State { final l10n = AppLocalizations.of(context)!; return Scaffold( - backgroundColor: TemaApp.colorFondo, - body: SafeArea( + body: FondoFarolero( + intenso: true, + child: SafeArea( child: Padding( padding: const EdgeInsets.all(24), child: Column( @@ -58,15 +60,18 @@ class _PantallaPalabraClienteState extends State { duration: const Duration(milliseconds: 300), width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 48, horizontal: 24), - decoration: BoxDecoration( + decoration: TemaApp.decoracionPanel( color: _palabraVisible - ? TemaApp.colorAcento + ? TemaApp.colorSuperficie : TemaApp.colorTarjeta, - borderRadius: BorderRadius.circular(24), + borderColor: _palabraVisible + ? TemaApp.colorNaranja + : TemaApp.colorBorde, + ).copyWith( boxShadow: _palabraVisible ? [ BoxShadow( - color: TemaApp.colorAcento.withValues(alpha: 0.4), + color: TemaApp.colorNaranja.withValues(alpha: 0.32), blurRadius: 24, spreadRadius: 2, ), @@ -83,17 +88,17 @@ class _PantallaPalabraClienteState extends State { size: 32, ), const SizedBox(height: 16), - Text( - _palabraVisible ? widget.palabra : '???', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: _palabraVisible - ? Colors.white - : TemaApp.colorTextoSecundario, - ), - ), + _palabraVisible + ? TarjetaPalabraFarolero(palabra: widget.palabra) + : const Text( + '???', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: TemaApp.colorTextoSecundario, + ), + ), ], ), ), @@ -164,6 +169,7 @@ class _PantallaPalabraClienteState extends State { ), ), ), + ), ); } } diff --git a/lib/pantallas/pantalla_palabras_cliente.dart b/lib/pantallas/pantalla_palabras_cliente.dart index fff1b35..37fbb76 100644 --- a/lib/pantallas/pantalla_palabras_cliente.dart +++ b/lib/pantallas/pantalla_palabras_cliente.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; import 'package:farolero/modelos/inicio_partida_multijugador.dart'; +import 'package:farolero/tema/componentes_farolero.dart'; import 'package:farolero/tema/tema_app.dart'; /// Reveal secuencial para clientes que manejan uno o varios jugadores. @@ -44,8 +45,9 @@ class _PantallaPalabrasClienteState extends State { final actual = _actual; return Scaffold( - backgroundColor: TemaApp.colorFondo, - body: SafeArea( + body: FondoFarolero( + intenso: true, + child: SafeArea( child: Padding( padding: const EdgeInsets.all(24), child: Column( @@ -71,8 +73,11 @@ class _PantallaPalabrasClienteState extends State { horizontal: 24, ), decoration: BoxDecoration( - color: _visible ? TemaApp.colorAcento : TemaApp.colorTarjeta, - borderRadius: BorderRadius.circular(24), + color: _visible ? TemaApp.colorSuperficie : TemaApp.colorTarjeta, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: _visible ? TemaApp.colorNaranja : TemaApp.colorBorde, + ), ), child: Column( children: [ @@ -82,21 +87,20 @@ class _PantallaPalabrasClienteState extends State { size: 32, ), const SizedBox(height: 16), - Text( - _visible - ? (actual.esImpostor - ? l10n.youAreImpostor - : actual.palabra ?? '') - : '???', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: _visible - ? Colors.white - : TemaApp.colorTextoSecundario, + if (_visible && !actual.esImpostor) + TarjetaPalabraFarolero(palabra: actual.palabra ?? '') + else + Text( + _visible ? l10n.youAreImpostor : '???', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: _visible + ? TemaApp.colorDorado + : TemaApp.colorTextoSecundario, + ), ), - ), ], ), ), @@ -123,6 +127,7 @@ class _PantallaPalabrasClienteState extends State { ), ), ), + ), ); } } diff --git a/lib/pantallas/pantalla_principal.dart b/lib/pantallas/pantalla_principal.dart index 9fa7706..817bdf8 100644 --- a/lib/pantallas/pantalla_principal.dart +++ b/lib/pantallas/pantalla_principal.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_ajustes.dart'; import 'pantalla_crear_partida.dart'; @@ -14,134 +15,152 @@ class PantallaPrincipal extends StatelessWidget { final l10n = AppLocalizations.of(context)!; return Scaffold( - body: SafeArea( - child: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Logo - Container( - width: 120, - height: 120, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: const LinearGradient( - colors: [TemaApp.colorAcento, TemaApp.colorNaranja], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + body: FondoFarolero( + intenso: true, + child: SafeArea( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 18), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 420), + child: Column( + children: [ + Row( + children: [ + const AvatarFarolero(texto: 'A', size: 48), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Alex', + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + 'Nivel 12', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 3), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: 0.68, + minHeight: 4, + color: TemaApp.colorPurpura, + backgroundColor: Colors.black.withValues(alpha: 0.45), + ), + ), + ], + ), + ), + IconButton.filledTonal( + tooltip: l10n.settings, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const PantallaAjustes(), + ), + ); + }, + icon: const Icon(Icons.settings), + ), + ], ), - boxShadow: [ - BoxShadow( - color: TemaApp.colorAcento.withValues(alpha: 0.4), - blurRadius: 30, - spreadRadius: 5, + const SizedBox(height: 38), + const LogoFarolero(size: 70), + const SizedBox(height: 12), + Text( + l10n.subtitle, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: TemaApp.colorTexto, + fontSize: 15, ), - ], - ), - child: const Center( - child: Text( - '🎭', - style: TextStyle(fontSize: 56), ), - ), - ), - const SizedBox(height: 24), - - // Título - Text( - l10n.appTitle, - style: Theme.of(context).textTheme.headlineLarge?.copyWith( - fontSize: 36, - letterSpacing: 1.2, - ), - ), - const SizedBox(height: 8), - Text( - l10n.subtitle, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontSize: 16, - ), - ), - const SizedBox(height: 48), - - // Botones - SizedBox( - width: double.infinity, - child: ElevatedButton.icon( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const PantallaCrearPartida(), + const SizedBox(height: 54), + BotonFarolero( + texto: 'Jugar', + icono: Icons.play_arrow, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const PantallaCrearPartida(), + ), + ); + }, + ), + const SizedBox(height: 12), + BotonFarolero.secundario( + texto: l10n.joinGame, + icono: Icons.bolt, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const PantallaUnirse(), + ), + ); + }, + ), + const SizedBox(height: 12), + BotonFarolero.oscuro( + texto: l10n.howToPlay, + icono: Icons.question_mark, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const PantallaReglas(), + ), + ); + }, + ), + const SizedBox(height: 18), + Row( + children: [ + Expanded( + child: AccesoFarolero( + etiqueta: 'Historial', + icono: Icons.history, + onPressed: () {}, + ), ), - ); - }, - icon: const Text('🎮', style: TextStyle(fontSize: 20)), - label: Text(l10n.createGame), - ), - ), - const SizedBox(height: 16), - - SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const PantallaUnirse(), + const SizedBox(width: 8), + Expanded( + child: AccesoFarolero( + etiqueta: 'Logros', + icono: Icons.emoji_events, + onPressed: () {}, + ), ), - ); - }, - icon: const Text('📱', style: TextStyle(fontSize: 20)), - label: Text(l10n.joinGame), - ), - ), - const SizedBox(height: 16), - - SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const PantallaReglas(), + const SizedBox(width: 8), + Expanded( + child: AccesoFarolero( + etiqueta: 'Ranking', + icono: Icons.bar_chart, + onPressed: () {}, + ), ), - ); - }, - icon: const Text('📖', style: TextStyle(fontSize: 20)), - label: Text(l10n.howToPlay), - ), - ), - const SizedBox(height: 16), - - SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const PantallaAjustes(), + const SizedBox(width: 8), + Expanded( + child: AccesoFarolero( + etiqueta: 'Tienda', + icono: Icons.storefront, + onPressed: () {}, + ), ), - ); - }, - icon: const Icon(Icons.settings, size: 20), - label: Text(l10n.settings), - ), + ], + ), + const SizedBox(height: 28), + Text( + l10n.playersRange, + style: Theme.of(context).textTheme.bodySmall, + ), + ], ), - const SizedBox(height: 48), - - Text( - l10n.playersRange, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontSize: 12, - ), - ), - ], + ), ), ), ), diff --git a/lib/pantallas/pantalla_reglas.dart b/lib/pantallas/pantalla_reglas.dart index 9318e18..f7020cd 100644 --- a/lib/pantallas/pantalla_reglas.dart +++ b/lib/pantallas/pantalla_reglas.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:farolero/l10n/generated/app_localizations.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; class PantallaReglas extends StatelessWidget { @@ -11,56 +12,117 @@ class PantallaReglas extends StatelessWidget { return Scaffold( appBar: AppBar(title: Text(l10n.rulesTitle)), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _seccion(context, l10n.rulesWhatIsTitle, l10n.rulesWhatIsBody), - _seccion(context, l10n.rulesHowToPlayTitle, l10n.rulesHowToPlayBody), - _seccion(context, l10n.rulesWhoWinsTitle, l10n.rulesWhoWinsBody), - _seccion(context, l10n.rulesTipsPlayersTitle, l10n.rulesTipsPlayersBody), - _seccion(context, l10n.rulesTipsImpostorTitle, l10n.rulesTipsImpostorBody), - _seccion(context, l10n.rulesModesTitle, l10n.rulesModesBody), + _seccion( + context, + 1, + Icons.person_search, + l10n.rulesWhatIsTitle, + l10n.rulesWhatIsBody, + ), + _seccion( + context, + 2, + Icons.chat_bubble, + l10n.rulesHowToPlayTitle, + l10n.rulesHowToPlayBody, + ), + _seccion( + context, + 3, + Icons.how_to_vote, + l10n.rulesWhoWinsTitle, + l10n.rulesWhoWinsBody, + ), + _seccion( + context, + 4, + Icons.lightbulb, + l10n.rulesTipsPlayersTitle, + l10n.rulesTipsPlayersBody, + ), + _seccion( + context, + 5, + Icons.theater_comedy, + l10n.rulesTipsImpostorTitle, + l10n.rulesTipsImpostorBody, + ), + _seccion( + context, + 6, + Icons.devices, + l10n.rulesModesTitle, + l10n.rulesModesBody, + ), _ejemplo(context, l10n.rulesExampleTitle, l10n.rulesExampleBody), const SizedBox(height: 32), ], - ), - ), - ); - } - - Widget _seccion(BuildContext context, String titulo, String contenido) { - return Padding( - padding: const EdgeInsets.only(bottom: 16), - child: Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(titulo, - style: Theme.of(context).textTheme.titleLarge), - const SizedBox(height: 8), - Text(contenido, - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - height: 1.5, - )), - ], ), ), ), ); } + Widget _seccion( + BuildContext context, + int numero, + IconData icono, + String titulo, + String contenido, + ) { + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: PanelFarolero( + padding: const EdgeInsets.all(12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 54, + height: 54, + decoration: BoxDecoration( + color: TemaApp.colorNaranja.withValues(alpha: 0.16), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: TemaApp.colorNaranja), + ), + child: Icon(icono, color: TemaApp.colorNaranja, size: 30), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$numero. ${titulo.toUpperCase()}', + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 8), + Text(contenido, + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + height: 1.5, + )), + ], + ), + ), + ], + ), + ), + ); + } + Widget _ejemplo(BuildContext context, String titulo, String contenido) { return Padding( padding: const EdgeInsets.only(bottom: 16), - child: Card( + child: PanelFarolero( color: TemaApp.colorNaranja.withValues(alpha: 0.15), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( + borderColor: TemaApp.colorNaranja, + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(titulo, @@ -73,7 +135,6 @@ class PantallaReglas extends StatelessWidget { height: 1.5, )), ], - ), ), ), ); diff --git a/lib/pantallas/pantalla_resultado.dart b/lib/pantallas/pantalla_resultado.dart index 302e323..51c4b5a 100644 --- a/lib/pantallas/pantalla_resultado.dart +++ b/lib/pantallas/pantalla_resultado.dart @@ -3,6 +3,7 @@ import 'package:farolero/l10n/generated/app_localizations.dart'; import 'package:provider/provider.dart'; import '../estado/estado_juego.dart'; import '../modelos/partida.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_adivinanza.dart'; import 'pantalla_debate.dart'; @@ -62,10 +63,11 @@ class _PantallaResultadoState extends State title: Text(l10n.result), automaticallyImplyLeading: false, ), - body: Center( - child: SingleChildScrollView( - padding: const EdgeInsets.all(32), - child: Column( + body: FondoFarolero( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(32), + child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Animación de suspense @@ -172,6 +174,7 @@ class _PantallaResultadoState extends State ), ], ], + ), ), ), ), diff --git a/lib/pantallas/pantalla_unirse.dart b/lib/pantallas/pantalla_unirse.dart index 815d470..d5c4aed 100644 --- a/lib/pantallas/pantalla_unirse.dart +++ b/lib/pantallas/pantalla_unirse.dart @@ -7,6 +7,7 @@ import '../modelos/inicio_partida_multijugador.dart'; import '../modelos/usuario.dart'; import '../servicios/servicio_nearby.dart'; import '../servicios/servicio_permisos.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_palabra_cliente.dart'; import 'pantalla_palabras_cliente.dart'; @@ -323,14 +324,19 @@ class _PantallaUnirseState extends State { Widget _buildFormularioNombre(BuildContext context, AppLocalizations l10n) { return Scaffold( appBar: AppBar(title: Text(l10n.joinGameTitle)), - body: Padding( - padding: const EdgeInsets.all(32), - child: Form( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(32), + child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text('📱', style: TextStyle(fontSize: 64)), + const Icon( + Icons.bluetooth_searching, + color: TemaApp.colorAzul, + size: 70, + ), const SizedBox(height: 24), Text( l10n.joinGameTitle, @@ -373,6 +379,7 @@ class _PantallaUnirseState extends State { ), ), ), + ), ); } @@ -399,9 +406,10 @@ class _PantallaUnirseState extends State { }, ), ), - body: Padding( - padding: const EdgeInsets.all(24), - child: Column( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( children: [ // Estado if (_conectando) ...[ @@ -495,6 +503,7 @@ class _PantallaUnirseState extends State { ], ), ), + ), ); } @@ -610,9 +619,10 @@ class _PantallaUnirseState extends State { }, ), ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(24), - child: Column( + body: FondoFarolero( + child: SingleChildScrollView( + padding: const EdgeInsets.all(24), + child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Estado de conexión @@ -677,6 +687,7 @@ class _PantallaUnirseState extends State { ), ], ], + ), ), ), ); diff --git a/lib/pantallas/pantalla_ver_palabra.dart b/lib/pantallas/pantalla_ver_palabra.dart index 16a8845..8799e9c 100644 --- a/lib/pantallas/pantalla_ver_palabra.dart +++ b/lib/pantallas/pantalla_ver_palabra.dart @@ -3,6 +3,7 @@ import 'package:farolero/l10n/generated/app_localizations.dart'; import 'package:provider/provider.dart'; import '../estado/estado_juego.dart'; import '../modelos/palabra.dart'; +import '../tema/componentes_farolero.dart'; import '../tema/tema_app.dart'; import 'pantalla_debate.dart'; @@ -31,9 +32,10 @@ class _PantallaVerPalabraState extends State { title: Text(l10n.seeYourWord), automaticallyImplyLeading: false, ), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( children: [ Text( l10n.eachPlayerMustSee, @@ -109,6 +111,7 @@ class _PantallaVerPalabraState extends State { ), ), ], + ), ), ), ); @@ -169,10 +172,12 @@ class _PantallaRevelarPalabraState extends State<_PantallaRevelarPalabra> { return Scaffold( appBar: AppBar(title: Text(widget.nombre)), - body: Center( - child: Padding( - padding: const EdgeInsets.all(32), - child: Column( + body: FondoFarolero( + intenso: true, + child: Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( @@ -225,17 +230,7 @@ class _PantallaRevelarPalabraState extends State<_PantallaRevelarPalabra> { ), if (!widget.esImpostor) ...[ const SizedBox(height: 12), - Text( - widget.palabra, - style: Theme.of(context) - .textTheme - .headlineLarge - ?.copyWith( - fontSize: 32, - color: Colors.white, - ), - textAlign: TextAlign.center, - ), + TarjetaPalabraFarolero(palabra: widget.palabra), ], if (widget.esImpostor && widget.pistaActiva) ...[ const SizedBox(height: 12), @@ -324,6 +319,7 @@ class _PantallaRevelarPalabraState extends State<_PantallaRevelarPalabra> { ), ), ), + ), ); } } diff --git a/lib/pantallas/pantalla_votacion.dart b/lib/pantallas/pantalla_votacion.dart index 6591965..f986535 100644 --- a/lib/pantallas/pantalla_votacion.dart +++ b/lib/pantallas/pantalla_votacion.dart @@ -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_resultado.dart'; @@ -58,9 +59,10 @@ class _PantallaVotacionState extends State { title: Text(l10n.voting), automaticallyImplyLeading: false, ), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( + body: FondoFarolero( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( children: [ // Progreso de votos Container( @@ -161,6 +163,7 @@ class _PantallaVotacionState extends State { ), ), ], + ), ), ), ); @@ -174,10 +177,11 @@ class _PantallaVotacionState extends State { title: Text(l10n.votingComplete), automaticallyImplyLeading: false, ), - body: Center( - child: Padding( - padding: const EdgeInsets.all(32), - child: Column( + body: FondoFarolero( + child: Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('🗳️', style: TextStyle(fontSize: 64)), @@ -213,6 +217,7 @@ class _PantallaVotacionState extends State { ), ), ], + ), ), ), ), diff --git a/lib/tema/componentes_farolero.dart b/lib/tema/componentes_farolero.dart new file mode 100644 index 0000000..479856a --- /dev/null +++ b/lib/tema/componentes_farolero.dart @@ -0,0 +1,426 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'tema_app.dart'; + +class FondoFarolero extends StatelessWidget { + final Widget child; + final bool intenso; + + const FondoFarolero({ + super.key, + required this.child, + this.intenso = false, + }); + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: const BoxDecoration(gradient: TemaApp.gradienteFondo), + child: CustomPaint( + painter: _FondoFaroleroPainter(intenso: intenso), + child: child, + ), + ); + } +} + +class PanelFarolero extends StatelessWidget { + final Widget child; + final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? margin; + final Color? color; + final Color? borderColor; + + const PanelFarolero({ + super.key, + required this.child, + this.padding = const EdgeInsets.all(16), + this.margin, + this.color, + this.borderColor, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: margin, + padding: padding, + decoration: TemaApp.decoracionPanel(color: color, borderColor: borderColor), + child: child, + ); + } +} + +class LogoFarolero extends StatelessWidget { + final double size; + + const LogoFarolero({super.key, this.size = 64}); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + Positioned( + top: 0, + child: Icon( + Icons.lightbulb, + color: TemaApp.colorDorado.withValues(alpha: 0.32), + size: size * 0.82, + ), + ), + Text( + 'FAROLERO', + style: GoogleFonts.bangers( + fontSize: size, + color: TemaApp.colorNaranja, + letterSpacing: 0, + shadows: const [ + Shadow(offset: Offset(3, 4), blurRadius: 0, color: Color(0xFF5E1205)), + Shadow(offset: Offset(0, 0), blurRadius: 16, color: Color(0xFFFFC247)), + ], + ), + ), + ], + ); + } +} + +class BotonFarolero extends StatelessWidget { + final String texto; + final IconData icono; + final VoidCallback? onPressed; + final LinearGradient gradient; + final Color foreground; + + const BotonFarolero({ + super.key, + required this.texto, + required this.icono, + required this.onPressed, + this.gradient = TemaApp.gradientePrimario, + this.foreground = Colors.black, + }); + + const BotonFarolero.secundario({ + super.key, + required this.texto, + required this.icono, + required this.onPressed, + }) : gradient = const LinearGradient( + colors: [TemaApp.colorPurpura, Color(0xFF2B1736)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + foreground = Colors.white; + + const BotonFarolero.oscuro({ + super.key, + required this.texto, + required this.icono, + required this.onPressed, + }) : gradient = const LinearGradient( + colors: [Color(0xFF151F27), Color(0xFF090E13)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + foreground = TemaApp.colorTexto; + + @override + Widget build(BuildContext context) { + final habilitado = onPressed != null; + + return Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: onPressed, + child: Ink( + height: 54, + decoration: BoxDecoration( + gradient: habilitado + ? gradient + : const LinearGradient( + colors: [TemaApp.colorTarjeta, TemaApp.colorSuperficie], + ), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: habilitado + ? TemaApp.colorDorado.withValues(alpha: 0.74) + : TemaApp.colorBorde, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.34), + blurRadius: 14, + offset: const Offset(0, 8), + ), + ], + ), + child: 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.w800, + ), + ), + ), + const SizedBox(width: 58), + ], + ), + ), + ), + ); + } +} + +class AccesoFarolero extends StatelessWidget { + final String etiqueta; + final IconData icono; + final VoidCallback onPressed; + + const AccesoFarolero({ + super.key, + required this.etiqueta, + required this.icono, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return Material( + color: Colors.transparent, + child: InkWell( + borderRadius: BorderRadius.circular(8), + onTap: onPressed, + child: Ink( + height: 66, + decoration: TemaApp.decoracionPanel(color: TemaApp.colorSuperficie), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icono, color: TemaApp.colorNaranja, size: 22), + const SizedBox(height: 5), + Text( + etiqueta.toUpperCase(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: TemaApp.colorDorado, + fontSize: 11, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ), + ); + } +} + +class TarjetaPalabraFarolero extends StatelessWidget { + final String palabra; + + const TarjetaPalabraFarolero({super.key, required this.palabra}); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 28), + decoration: BoxDecoration( + color: const Color(0xFFC48642), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFF6B3519), width: 2), + boxShadow: [ + BoxShadow( + color: TemaApp.colorNaranja.withValues(alpha: 0.28), + blurRadius: 24, + ), + ], + ), + child: Text( + palabra.toUpperCase(), + textAlign: TextAlign.center, + style: GoogleFonts.oswald( + color: const Color(0xFF1B0C05), + fontSize: 42, + fontWeight: FontWeight.w900, + letterSpacing: 0, + ), + ), + ); + } +} + +class AvatarFarolero extends StatelessWidget { + final String texto; + final Color color; + final double size; + + const AvatarFarolero({ + super.key, + required this.texto, + this.color = TemaApp.colorNaranja, + this.size = 40, + }); + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: RadialGradient( + colors: [color.withValues(alpha: 0.9), TemaApp.colorSuperficie], + ), + border: Border.all(color: TemaApp.colorDorado, width: 2), + ), + child: Center( + child: Text( + texto, + style: TextStyle( + color: TemaApp.colorTexto, + fontWeight: FontWeight.bold, + fontSize: size * 0.36, + ), + ), + ), + ); + } +} + +class _FondoFaroleroPainter extends CustomPainter { + final bool intenso; + + const _FondoFaroleroPainter({required this.intenso}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint()..isAntiAlias = true; + final alto = size.height; + final ancho = size.width; + + paint.color = const Color(0xFF152845).withValues(alpha: intenso ? 0.34 : 0.22); + canvas.drawCircle(Offset(ancho * 0.78, alto * 0.16), 18, paint); + + paint.color = const Color(0xFF07101A).withValues(alpha: 0.82); + final colinas = Path() + ..moveTo(0, alto * 0.34) + ..quadraticBezierTo(ancho * 0.28, alto * 0.21, ancho * 0.55, alto * 0.33) + ..quadraticBezierTo(ancho * 0.82, alto * 0.43, ancho, alto * 0.26) + ..lineTo(ancho, alto) + ..lineTo(0, alto) + ..close(); + canvas.drawPath(colinas, paint); + + _dibujarCasas(canvas, size, paint); + _dibujarFarol(canvas, size, paint); + + paint.shader = RadialGradient( + colors: [ + TemaApp.colorNaranja.withValues(alpha: intenso ? 0.26 : 0.16), + Colors.transparent, + ], + ).createShader(Rect.fromCircle(center: Offset(ancho * 0.52, alto * 0.36), radius: 160)); + canvas.drawCircle(Offset(ancho * 0.52, alto * 0.36), 160, paint); + paint.shader = null; + } + + void _dibujarCasas(Canvas canvas, Size size, Paint paint) { + final alto = size.height; + final ancho = size.width; + paint.color = const Color(0xFF020407).withValues(alpha: 0.72); + + for (var i = 0; i < 5; i++) { + final w = ancho * (0.16 + i * 0.018); + final h = alto * (0.18 + (i % 2) * 0.05); + final x = -30 + i * ancho * 0.24; + final y = alto * (0.72 - i * 0.02); + final casa = Rect.fromLTWH(x, y - h, w, h); + canvas.drawRect(casa, paint); + final tejado = Path() + ..moveTo(x - 8, y - h) + ..lineTo(x + w * 0.48, y - h - 38) + ..lineTo(x + w + 8, y - h) + ..close(); + canvas.drawPath(tejado, paint); + + final ventana = Paint() + ..color = TemaApp.colorNaranja.withValues(alpha: 0.38) + ..isAntiAlias = true; + for (var j = 0; j < 2; j++) { + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH(x + 18 + j * 34, y - h + 36, 12, 22), + const Radius.circular(2), + ), + ventana, + ); + } + } + } + + void _dibujarFarol(Canvas canvas, Size size, Paint paint) { + final alto = size.height; + final ancho = size.width; + final centro = Offset(ancho * 0.5, alto * 0.28); + final glow = Paint() + ..shader = RadialGradient( + colors: [ + TemaApp.colorNaranja.withValues(alpha: 0.44), + Colors.transparent, + ], + ).createShader(Rect.fromCircle(center: centro, radius: 92)); + canvas.drawCircle(centro, 92, glow); + + paint + ..shader = null + ..style = PaintingStyle.stroke + ..strokeWidth = 3 + ..color = const Color(0xFF050507).withValues(alpha: 0.82); + canvas.drawArc( + Rect.fromCircle(center: centro.translate(0, -16), radius: 35), + math.pi, + math.pi, + false, + paint, + ); + paint.style = PaintingStyle.fill; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromCenter(center: centro, width: 38, height: 54), + const Radius.circular(5), + ), + paint, + ); + paint.color = TemaApp.colorNaranja.withValues(alpha: 0.82); + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromCenter(center: centro, width: 21, height: 34), + const Radius.circular(4), + ), + paint, + ); + } + + @override + bool shouldRepaint(covariant _FondoFaroleroPainter oldDelegate) { + return oldDelegate.intenso != intenso; + } +} diff --git a/lib/tema/tema_app.dart b/lib/tema/tema_app.dart index 25bd18d..db682a6 100644 --- a/lib/tema/tema_app.dart +++ b/lib/tema/tema_app.dart @@ -2,114 +2,212 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; class TemaApp { - static const colorFondo = Color(0xFF121212); - static const colorSuperficie = Color(0xFF1E1E1E); - static const colorTarjeta = Color(0xFF2A2A2A); - static const colorAcento = Color(0xFFE53935); // Rojo impostor - static const colorAcentoClaro = Color(0xFFFF6F61); - static const colorNaranja = Color(0xFFFF9800); - static const colorVerde = Color(0xFF4CAF50); + static const colorFondo = Color(0xFF05080D); + static const colorFondoAzul = Color(0xFF0A1520); + static const colorSuperficie = Color(0xFF0D151C); + static const colorTarjeta = Color(0xFF121B23); + static const colorBorde = Color(0xFF263947); + static const colorAcento = Color(0xFFC02824); + static const colorAcentoClaro = Color(0xFFF06A1A); + static const colorNaranja = Color(0xFFF49A13); + static const colorDorado = Color(0xFFFFCE55); + static const colorPurpura = Color(0xFF65306E); + static const colorAzul = Color(0xFF235BCE); + static const colorVerde = Color(0xFF61B944); static const colorTexto = Color(0xFFFFFFFF); - static const colorTextoSecundario = Color(0xFFB0B0B0); + static const colorTextoSecundario = Color(0xFFC2B9AA); + + static const gradientePrimario = LinearGradient( + colors: [Color(0xFFFFB11A), Color(0xFFE87A08)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ); + + static const gradientePeligro = LinearGradient( + colors: [Color(0xFFC02824), Color(0xFF741112)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ); + + static const gradienteFondo = LinearGradient( + colors: [Color(0xFF09131E), Color(0xFF030507)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ); static ThemeData obtenerTema() { + final base = ThemeData.dark(useMaterial3: true); + final cuerpo = GoogleFonts.robotoCondensedTextTheme(base.textTheme); + return ThemeData( useMaterial3: true, brightness: Brightness.dark, scaffoldBackgroundColor: colorFondo, colorScheme: const ColorScheme.dark( - primary: colorAcento, + primary: colorNaranja, secondary: colorNaranja, surface: colorSuperficie, error: colorAcento, + onPrimary: Colors.black, + onSurface: colorTexto, ), - textTheme: GoogleFonts.poppinsTextTheme( - const TextTheme( + textTheme: cuerpo.copyWith( headlineLarge: TextStyle( + fontFamily: GoogleFonts.oswald().fontFamily, color: colorTexto, fontWeight: FontWeight.bold, - fontSize: 28, + fontSize: 30, + letterSpacing: 0, ), headlineMedium: TextStyle( + fontFamily: GoogleFonts.oswald().fontFamily, color: colorTexto, fontWeight: FontWeight.bold, fontSize: 22, + letterSpacing: 0, ), titleLarge: TextStyle( - color: colorTexto, - fontWeight: FontWeight.w600, + fontFamily: GoogleFonts.oswald().fontFamily, + color: colorDorado, + fontWeight: FontWeight.w700, fontSize: 18, + letterSpacing: 0, ), titleMedium: TextStyle( + fontFamily: GoogleFonts.oswald().fontFamily, color: colorTexto, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontSize: 16, + letterSpacing: 0, ), - bodyLarge: TextStyle(color: colorTexto, fontSize: 16), - bodyMedium: TextStyle(color: colorTextoSecundario, fontSize: 14), - ), + bodyLarge: const TextStyle(color: colorTexto, fontSize: 16), + bodyMedium: const TextStyle(color: colorTextoSecundario, fontSize: 14), + bodySmall: const TextStyle(color: colorTextoSecundario, fontSize: 12), ), cardTheme: CardThemeData( - color: colorTarjeta, - elevation: 4, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + color: colorTarjeta.withValues(alpha: 0.82), + elevation: 0, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: const BorderSide(color: colorBorde), + ), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( - backgroundColor: colorAcento, - foregroundColor: colorTexto, - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - textStyle: GoogleFonts.poppins( - fontWeight: FontWeight.w600, + backgroundColor: colorNaranja, + foregroundColor: Colors.black, + disabledBackgroundColor: colorTarjeta, + disabledForegroundColor: colorTextoSecundario, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + textStyle: GoogleFonts.oswald( + fontWeight: FontWeight.w700, fontSize: 16, + letterSpacing: 0, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: colorTexto, - side: const BorderSide(color: colorAcento), - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + side: const BorderSide(color: colorBorde), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + textStyle: GoogleFonts.oswald( + fontWeight: FontWeight.w700, + fontSize: 16, + letterSpacing: 0, + ), ), ), inputDecorationTheme: InputDecorationTheme( filled: true, - fillColor: colorTarjeta, + fillColor: const Color(0xFF0B1117), border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: colorBorde), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: colorBorde), ), focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: colorAcento), + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: colorNaranja), ), labelStyle: const TextStyle(color: colorTextoSecundario), hintStyle: const TextStyle(color: colorTextoSecundario), ), appBarTheme: AppBarTheme( backgroundColor: colorFondo, - foregroundColor: colorTexto, + foregroundColor: colorNaranja, + centerTitle: true, elevation: 0, - titleTextStyle: GoogleFonts.poppins( - color: colorTexto, + titleTextStyle: GoogleFonts.oswald( + color: colorDorado, fontWeight: FontWeight.bold, fontSize: 20, + letterSpacing: 0, + ), + ), + dividerTheme: const DividerThemeData(color: colorBorde, thickness: 1), + listTileTheme: const ListTileThemeData( + iconColor: colorNaranja, + textColor: colorTexto, + subtitleTextStyle: TextStyle(color: colorTextoSecundario), + ), + segmentedButtonTheme: SegmentedButtonThemeData( + style: ButtonStyle( + backgroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) return colorNaranja; + return colorSuperficie; + }), + foregroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) return Colors.black; + return colorTexto; + }), + side: const WidgetStatePropertyAll(BorderSide(color: colorBorde)), + shape: WidgetStatePropertyAll( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), ), ), switchTheme: SwitchThemeData( thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) return colorAcento; + if (states.contains(WidgetState.selected)) return colorNaranja; return colorTextoSecundario; }), trackColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { - return colorAcento.withValues(alpha: 0.5); + return colorNaranja.withValues(alpha: 0.5); } return colorTarjeta; }), ), + snackBarTheme: SnackBarThemeData( + backgroundColor: colorTarjeta, + contentTextStyle: cuerpo.bodyMedium?.copyWith(color: colorTexto), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + ); + } + + static BoxDecoration decoracionPanel({ + Color? color, + Color? borderColor, + }) { + return BoxDecoration( + color: color ?? colorTarjeta.withValues(alpha: 0.84), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: borderColor ?? colorBorde), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.35), + blurRadius: 18, + offset: const Offset(0, 10), + ), + ], ); } } diff --git a/test/widget_test.dart b/test/widget_test.dart index a6e6f13..dd2e328 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -4,6 +4,6 @@ import 'package:farolero/main.dart'; void main() { testWidgets('App carga correctamente', (WidgetTester tester) async { await tester.pumpWidget(const FaroleroApp()); - expect(find.text('El Impostor'), findsOneWidget); + expect(find.text('FAROLERO'), findsOneWidget); }); }