Completo y absoluto cambio de diseño
All checks were successful
Build & Deploy Farolero / Análisis de código (push) Successful in 23s
Build & Deploy Farolero / Build APK + AAB release (push) Successful in 1m53s

This commit is contained in:
2026-05-04 13:57:55 +02:00
parent ab0d4dc2ba
commit 841f94e543
18 changed files with 1012 additions and 319 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -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,
),
],
),
),
),
);

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 '../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<PantallaAjustes> {
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<PantallaAjustes> {
),
const SizedBox(height: 16),
],
),
),
),
);

View File

@@ -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<PantallaCrearPartida> {
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<PantallaCrearPartida> {
),
const SizedBox(height: 16),
],
),
),
),
);

View File

@@ -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<PantallaDebate> {
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<PantallaDebate> {
],
),
],
),
),
),
);

View File

@@ -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<PantallaGestorHost> {
),
],
),
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<PantallaGestorHost> {
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
),
),
],
),
),
),
),

View File

@@ -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<PantallaLobbyHost> {
},
),
),
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<PantallaLobbyHost> {
),
),
],
),
),
),
);
@@ -176,6 +179,7 @@ class _PantallaLobbyHostState extends State<PantallaLobbyHost> {
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<PantallaLobbyHost> {
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(

View File

@@ -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<PantallaPalabraCliente> {
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<PantallaPalabraCliente> {
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<PantallaPalabraCliente> {
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<PantallaPalabraCliente> {
),
),
),
),
);
}
}

View File

@@ -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<PantallaPalabrasCliente> {
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<PantallaPalabrasCliente> {
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<PantallaPalabrasCliente> {
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<PantallaPalabrasCliente> {
),
),
),
),
);
}
}

View File

@@ -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,
),
),
],
),
),
),
),

View File

@@ -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,
)),
],
),
),
),
);

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 '../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<PantallaResultado>
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<PantallaResultado>
),
],
],
),
),
),
),

View File

@@ -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<PantallaUnirse> {
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<PantallaUnirse> {
),
),
),
),
);
}
@@ -399,9 +406,10 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
},
),
),
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<PantallaUnirse> {
],
),
),
),
);
}
@@ -610,9 +619,10 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
},
),
),
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<PantallaUnirse> {
),
],
],
),
),
),
);

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 '../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<PantallaVerPalabra> {
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<PantallaVerPalabra> {
),
),
],
),
),
),
);
@@ -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> {
),
),
),
),
);
}
}

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_resultado.dart';
@@ -58,9 +59,10 @@ class _PantallaVotacionState extends State<PantallaVotacion> {
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<PantallaVotacion> {
),
),
],
),
),
),
);
@@ -174,10 +177,11 @@ class _PantallaVotacionState extends State<PantallaVotacion> {
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<PantallaVotacion> {
),
),
],
),
),
),
),

View File

@@ -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;
}
}

View File

@@ -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),
),
],
);
}
}

View File

@@ -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);
});
}