fix: multidispositivo - Random seguro + gestor host + reacción clientes
- Random.secure() para selección de impostores (no predecible) - Random.secure() también en desempate de votación - Nueva PantallaGestorHost para coordinación multi-device - Navegación: host va a gestor tras iniciar, no a pantalla de palabra - PantallaPalabraCliente: cada jugador ve su palabra en su móvil - PantallaDebateCliente: debate con timer y botón solicitar votación - PantallaVotacionCliente: voto desde el móvil del cliente - PantallaUnirse: listener que reacciona a partidaInicio y cambia de fase - Protocolo: listo/voto/solicitoVotacion via Nearby hacia el host - Nuevas cadenas l10n ES
This commit is contained in:
@@ -2,9 +2,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:farolero/l10n/generated/app_localizations.dart';
|
||||
import '../modelos/jugador.dart';
|
||||
import '../servicios/servicio_nearby.dart';
|
||||
import '../servicios/servicio_permisos.dart';
|
||||
import '../tema/tema_app.dart';
|
||||
import 'pantalla_palabra_cliente.dart';
|
||||
import 'pantalla_debate_cliente.dart';
|
||||
import 'pantalla_votacion_cliente.dart';
|
||||
|
||||
/// Pantalla para unirse a una partida multidispositivo.
|
||||
/// Flujo: nombre → discovery automático (lista de salas) → fallback QR
|
||||
@@ -26,6 +30,110 @@ class _PantallaUnirseState extends State<PantallaUnirse> {
|
||||
String? _error;
|
||||
String? _salaSeleccionada;
|
||||
|
||||
// Estado del juego recibido del host
|
||||
String? _palabraRecibida;
|
||||
bool _esImpostor = false;
|
||||
String? _pistaCategoria;
|
||||
final List<Jugador> _jugadores = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Registrar listener ANTES del primer build
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_registrarListenerPartida();
|
||||
});
|
||||
}
|
||||
|
||||
void _registrarListenerPartida() {
|
||||
final nearby = context.read<ServicioNearby>();
|
||||
nearby.onMensaje((endpointId, mensaje) {
|
||||
if (mensaje.tipo == TipoMensaje.partidaInicio) {
|
||||
// El host ha iniciado la partida — nos ha enviado nuestra palabra
|
||||
setState(() {
|
||||
_palabraRecibida = mensaje.datos['palabra'] as String?;
|
||||
_esImpostor = mensaje.datos['esImpostor'] as bool? ?? false;
|
||||
_pistaCategoria = mensaje.datos['categoria'] as String?;
|
||||
});
|
||||
// Navegar a pantalla de palabra del cliente
|
||||
if (mounted && _palabraRecibida != null) {
|
||||
_navegarAPalabra();
|
||||
}
|
||||
} else if (mensaje.tipo == TipoMensaje.fase) {
|
||||
// El host cambia de fase — navegar a la pantalla correspondiente
|
||||
final fase = mensaje.datos['fase'] as String?;
|
||||
if (mounted && fase != null) {
|
||||
_navegarSegunFase(fase);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _navegarAPalabra() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PantallaPalabraCliente(
|
||||
palabra: _palabraRecibida ?? '',
|
||||
esImpostor: _esImpostor,
|
||||
pistaCategoria: _pistaCategoria,
|
||||
onVisto: () {
|
||||
// Enviar "listo" al host y volver a la espera
|
||||
final nearby = context.read<ServicioNearby>();
|
||||
if (nearby.hostEndpointId != null) {
|
||||
nearby.enviarMensaje(
|
||||
nearby.hostEndpointId!,
|
||||
MensajeP2P(tipo: TipoMensaje.listo, datos: {}),
|
||||
);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _navegarSegunFase(String fase) {
|
||||
switch (fase) {
|
||||
case 'debate':
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PantallaDebateCliente(
|
||||
tiempoDebateSegundos: null,
|
||||
onSolicitarVotacion: () {
|
||||
final nearby = context.read<ServicioNearby>();
|
||||
if (nearby.hostEndpointId != null) {
|
||||
nearby.enviarMensaje(
|
||||
nearby.hostEndpointId!,
|
||||
MensajeP2P(tipo: TipoMensaje.ping, datos: {'solicitoVotacion': true}),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case 'votacion':
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PantallaVotacionCliente(
|
||||
jugadores: _jugadores,
|
||||
onVoto: (votoporId) {
|
||||
final nearby = context.read<ServicioNearby>();
|
||||
if (nearby.hostEndpointId != null) {
|
||||
nearby.enviarMensaje(
|
||||
nearby.hostEndpointId!,
|
||||
MensajeP2P(tipo: TipoMensaje.voto, datos: {'votoporId': votoporId}),
|
||||
);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nombreController.dispose();
|
||||
|
||||
Reference in New Issue
Block a user