Files
farolero/lib/pantallas/pantalla_fin_partida.dart
ShanaiaBot 1bca50af1d v0.2.0: i18n 18 idiomas + pantalla ajustes + bancos multiidioma
Internacionalización completa:
- 18 ficheros .arb: es, en, fr, pt, de, it, ru, ja, ko, zh, zh_TW, ar, hi, tr, pl, nl, ca, eu
- Todos los strings extraídos de todas las pantallas
- Detección automática de idioma del sistema
- Selector manual en pantalla de ajustes

Pantalla de ajustes nueva:
- Selector de idioma con banderas emoji
- Vibración ON/OFF
- Acerca de (versión, desarrollador)

Bancos de palabras multiidioma:
- palabras.json (castellano, 1000 palabras)
- palabras_en.json (inglés, 1000 palabras)
- palabras_fr.json (francés, 1000 palabras)
- Fallback a castellano si no hay banco del idioma

13138 líneas Dart, 39 ficheros, 0 issues en flutter analyze
2026-04-04 01:18:09 +02:00

244 lines
9.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:el_impostor/l10n/generated/app_localizations.dart';
import 'package:provider/provider.dart';
import '../estado/estado_juego.dart';
import '../modelos/palabra.dart';
import '../tema/tema_app.dart';
import 'pantalla_principal.dart';
import 'pantalla_ver_palabra.dart';
class PantallaFinPartida extends StatelessWidget {
const PantallaFinPartida({super.key});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final estado = context.watch<EstadoJuego>();
final partida = estado.partida;
if (partida == null) return const SizedBox.shrink();
final ganaronJugadores = partida.ganador == 'jugadores';
final impostores =
partida.jugadores.where((j) => j.esImpostor).toList();
return Scaffold(
appBar: AppBar(
title: Text(l10n.gameOver),
automaticallyImplyLeading: false,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
children: [
// Ganador
Container(
width: double.infinity,
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: ganaronJugadores
? [TemaApp.colorVerde.withValues(alpha: 0.3), TemaApp.colorVerde.withValues(alpha: 0.1)]
: [TemaApp.colorAcento.withValues(alpha: 0.3), TemaApp.colorAcento.withValues(alpha: 0.1)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: ganaronJugadores
? TemaApp.colorVerde
: TemaApp.colorAcento,
),
),
child: Column(
children: [
Text(
ganaronJugadores ? '🎉' : '🎭',
style: const TextStyle(fontSize: 64),
),
const SizedBox(height: 16),
Text(
ganaronJugadores
? l10n.playersWin
: l10n.impostorsWin,
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(
color: ganaronJugadores
? TemaApp.colorVerde
: TemaApp.colorAcento,
),
textAlign: TextAlign.center,
),
],
),
),
const SizedBox(height: 24),
// Palabra secreta
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Text(l10n.theSecretWordWas,
style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
Text(
partida.palabraSecreta,
style: Theme.of(context)
.textTheme
.headlineLarge
?.copyWith(
color: TemaApp.colorNaranja,
fontSize: 32,
),
),
const SizedBox(height: 4),
Text(
l10n.categoryLabel(BancoPalabras.nombreBonitoCategoria(partida.categoriaReal, l10n)),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
const SizedBox(height: 16),
// Impostores
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Text(
impostores.length == 1 ? l10n.theImpostorWas : l10n.theImpostorsWere,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
...impostores.map((j) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('🎭 ',
style: TextStyle(fontSize: 18)),
Text(
j.nombre,
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: TemaApp.colorAcento),
),
if (j.eliminado) ...[
const SizedBox(width: 8),
const Text('💀',
style: TextStyle(fontSize: 16)),
],
],
),
)),
],
),
),
),
const SizedBox(height: 16),
// Estadísticas de votaciones
if (partida.historialVotaciones.isNotEmpty)
Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(l10n.votingHistory,
style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 12),
...partida.historialVotaciones
.asMap()
.entries
.map((entrada) {
final ronda = entrada.key + 1;
final resultado = entrada.value;
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${l10n.roundElimination(ronda, resultado.eliminadoNombre)} ${resultado.eraImpostor ? '🎭' : '😇'}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: resultado.eraImpostor
? TemaApp.colorVerde
: TemaApp.colorAcento,
),
),
...resultado.votos.entries.map((v) {
final votante = partida.jugadores
.firstWhere((j) => j.id == v.key);
final votado = partida.jugadores
.firstWhere((j) => j.id == v.value);
return Text(
' ${votante.nombre}${votado.nombre}',
style: Theme.of(context)
.textTheme
.bodyMedium,
);
}),
],
),
);
}),
],
),
),
),
const SizedBox(height: 24),
// Botones
SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton.icon(
onPressed: () {
estado.revancha();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const PantallaVerPalabra(),
),
);
},
icon: const Icon(Icons.replay),
label: Text(l10n.rematch),
),
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
height: 56,
child: OutlinedButton.icon(
onPressed: () {
estado.limpiar();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (_) => const PantallaPrincipal(),
),
(route) => false,
);
},
icon: const Icon(Icons.home),
label: Text(l10n.mainMenu),
),
),
const SizedBox(height: 16),
],
),
),
);
}
}