Renombrado de 'El Impostor' a 'Farolero'. Package: es.freetimelab.farolero 18 idiomas: es, en, fr, pt, de, it, ru, ja, ko, zh, zh_TW, ar, hi, tr, pl, nl, ca, eu Bancos de palabras: es (1000), en (1000), fr (1000) Pantalla de ajustes con selector de idioma 13138 líneas Dart, 0 issues
222 lines
7.4 KiB
Dart
222 lines
7.4 KiB
Dart
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/tema_app.dart';
|
|
import 'pantalla_resultado.dart';
|
|
|
|
class PantallaVotacion extends StatefulWidget {
|
|
const PantallaVotacion({super.key});
|
|
|
|
@override
|
|
State<PantallaVotacion> createState() => _PantallaVotacionState();
|
|
}
|
|
|
|
class _PantallaVotacionState extends State<PantallaVotacion> {
|
|
String? _seleccionado;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final estado = context.watch<EstadoJuego>();
|
|
final partida = estado.partida;
|
|
if (partida == null) return const SizedBox.shrink();
|
|
|
|
final activos = partida.jugadoresActivos;
|
|
final todosVotaron = estado.todosHanVotado();
|
|
|
|
// Modo un solo móvil
|
|
if (!partida.config.modoMultimovil) {
|
|
return _construirVotacionUnMovil(context, estado, partida, activos, todosVotaron);
|
|
}
|
|
|
|
// Modo multimóvil sería similar pero controlado por Nearby
|
|
return _construirVotacionUnMovil(context, estado, partida, activos, todosVotaron);
|
|
}
|
|
|
|
Widget _construirVotacionUnMovil(
|
|
BuildContext context,
|
|
EstadoJuego estado,
|
|
partida,
|
|
List activos,
|
|
bool todosVotaron,
|
|
) {
|
|
// Encontrar el siguiente votante que no haya votado
|
|
final sinVotar = activos
|
|
.where((j) => !estado.votos.containsKey(j.id))
|
|
.toList();
|
|
|
|
if (todosVotaron) {
|
|
return _construirTodosVotaron(context, estado);
|
|
}
|
|
|
|
final l10n = AppLocalizations.of(context)!;
|
|
final votanteActual = sinVotar.isNotEmpty ? sinVotar[0] : activos[0];
|
|
final puedenRecibir = activos.where((j) => j.id != votanteActual.id).toList();
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(l10n.voting),
|
|
automaticallyImplyLeading: false,
|
|
),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
// Progreso de votos
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: TemaApp.colorTarjeta,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Text(
|
|
l10n.turnToVote,
|
|
style: Theme.of(context).textTheme.bodyMedium,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
votanteActual.nombre,
|
|
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
|
color: TemaApp.colorNaranja,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
l10n.votesProgress(estado.votos.length, activos.length),
|
|
style: Theme.of(context).textTheme.bodyMedium,
|
|
),
|
|
const SizedBox(height: 4),
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(4),
|
|
child: LinearProgressIndicator(
|
|
value: estado.votos.length / activos.length,
|
|
backgroundColor: TemaApp.colorSuperficie,
|
|
valueColor: const AlwaysStoppedAnimation(TemaApp.colorAcento),
|
|
minHeight: 6,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
Text(
|
|
l10n.whoIsImpostor,
|
|
style: Theme.of(context).textTheme.titleMedium,
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Lista de candidatos
|
|
Expanded(
|
|
child: ListView.builder(
|
|
itemCount: puedenRecibir.length,
|
|
itemBuilder: (context, index) {
|
|
final candidato = puedenRecibir[index];
|
|
final seleccionado = _seleccionado == candidato.id;
|
|
return Card(
|
|
color: seleccionado
|
|
? TemaApp.colorAcento.withValues(alpha: 0.3)
|
|
: TemaApp.colorTarjeta,
|
|
child: ListTile(
|
|
leading: CircleAvatar(
|
|
backgroundColor: seleccionado
|
|
? TemaApp.colorAcento
|
|
: TemaApp.colorSuperficie,
|
|
child: Text('${index + 1}',
|
|
style: const TextStyle(color: Colors.white)),
|
|
),
|
|
title: Text(candidato.nombre),
|
|
trailing: seleccionado
|
|
? const Icon(Icons.check_circle,
|
|
color: TemaApp.colorAcento)
|
|
: const Icon(Icons.radio_button_unchecked),
|
|
onTap: () {
|
|
setState(() => _seleccionado = candidato.id);
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: ElevatedButton.icon(
|
|
onPressed: _seleccionado != null
|
|
? () {
|
|
estado.registrarVoto(
|
|
votanteActual.id, _seleccionado!);
|
|
setState(() {
|
|
_seleccionado = null;
|
|
});
|
|
}
|
|
: null,
|
|
icon: const Icon(Icons.how_to_vote),
|
|
label: Text(l10n.confirmVote),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _construirTodosVotaron(BuildContext context, EstadoJuego estado) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(l10n.votingComplete),
|
|
automaticallyImplyLeading: false,
|
|
),
|
|
body: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(32),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Text('🗳️', style: TextStyle(fontSize: 64)),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
l10n.allVoted,
|
|
style: Theme.of(context).textTheme.headlineMedium,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
l10n.tapToReveal,
|
|
style: Theme.of(context).textTheme.bodyMedium,
|
|
),
|
|
const SizedBox(height: 32),
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: ElevatedButton.icon(
|
|
onPressed: () {
|
|
final resultado = estado.procesarVotacion();
|
|
if (resultado != null) {
|
|
Navigator.pushReplacement(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) =>
|
|
PantallaResultado(resultado: resultado),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
icon: const Icon(Icons.visibility),
|
|
label: Text(l10n.revealResult),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|