feat: permisos automáticos + cableado crear partida → lobby multi
- ServicioPermisos: solicita BT+Location automáticamente con diálogo si denegados - PantallaCrearPartida: modo multi → pide nombre host → permisos → lobby con QR - PantallaUnirse: pide permisos antes de iniciar discovery - ServicioNearby: pararBusqueda() para limpiar discovery sin desconectar - Botón iniciar habilitado en modo multi sin necesidad de 3 jugadores locales - permission_handler añadido como dependencia
This commit is contained in:
@@ -4,7 +4,10 @@ import 'package:provider/provider.dart';
|
||||
import '../estado/estado_juego.dart';
|
||||
import '../modelos/palabra.dart';
|
||||
import '../modelos/partida.dart';
|
||||
import '../servicios/servicio_nearby.dart';
|
||||
import '../servicios/servicio_permisos.dart';
|
||||
import '../tema/tema_app.dart';
|
||||
import 'pantalla_lobby_host.dart';
|
||||
import 'pantalla_ver_palabra.dart';
|
||||
|
||||
class PantallaCrearPartida extends StatefulWidget {
|
||||
@@ -66,6 +69,12 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
|
||||
|
||||
void _iniciarPartida() {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
if (_modoMultimovil) {
|
||||
_iniciarPartidaMulti();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_jugadores.length < 3) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.minPlayersRequired)),
|
||||
@@ -76,7 +85,7 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
|
||||
final estado = context.read<EstadoJuego>();
|
||||
estado.crearPartida(
|
||||
config: ConfigPartida(
|
||||
modoMultimovil: _modoMultimovil,
|
||||
modoMultimovil: false,
|
||||
categoria: _categoria,
|
||||
numImpostores: _numImpostores,
|
||||
pistaImpostor: _pistaImpostor,
|
||||
@@ -91,6 +100,120 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _iniciarPartidaMulti() async {
|
||||
// 1. Pedir permisos automáticamente
|
||||
final permisosOk = await ServicioPermisos.solicitarPermisosNearby(context);
|
||||
if (!permisosOk) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Se necesitan permisos de Bluetooth y ubicación')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Pedir nombre del host
|
||||
final nombre = await _pedirNombreHost();
|
||||
if (nombre == null || nombre.trim().isEmpty) return;
|
||||
|
||||
// 3. Iniciar host en Nearby
|
||||
if (!mounted) return;
|
||||
final nearby = context.read<ServicioNearby>();
|
||||
final nombreSala = '${nombre.trim()} - Farolero';
|
||||
final ok = await nearby.iniciarHost(nombreSala, nombre.trim());
|
||||
|
||||
if (!ok) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('No se pudo crear la sala. Verifica Bluetooth.')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Navegar al lobby con QR
|
||||
if (mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => PantallaLobbyHost(
|
||||
nombreSala: nombreSala,
|
||||
onIniciar: () {
|
||||
// Cuando el host toca "Iniciar" con suficientes jugadores
|
||||
final estado = context.read<EstadoJuego>();
|
||||
final jugadoresMulti = [
|
||||
nombre.trim(),
|
||||
...nearby.jugadores.map((j) => j.nombre),
|
||||
];
|
||||
estado.crearPartida(
|
||||
config: ConfigPartida(
|
||||
modoMultimovil: true,
|
||||
categoria: _categoria,
|
||||
numImpostores: _numImpostores,
|
||||
pistaImpostor: _pistaImpostor,
|
||||
tiempoDebateSegundos: _tiempoDebate,
|
||||
),
|
||||
nombresJugadores: jugadoresMulti,
|
||||
);
|
||||
|
||||
// Enviar palabras a cada jugador via Nearby
|
||||
final partida = estado.partida!;
|
||||
final impostores = <String, bool>{};
|
||||
for (int i = 0; i < nearby.jugadores.length; i++) {
|
||||
final jugadorNearby = nearby.jugadores[i];
|
||||
// El jugador [0] es el host, los de nearby son [1..n]
|
||||
final jugadorPartida = partida.jugadores[i + 1];
|
||||
impostores[jugadorNearby.endpointId] = jugadorPartida.esImpostor;
|
||||
}
|
||||
|
||||
nearby.enviarInicioPartida(
|
||||
palabraSecreta: partida.palabraSecreta,
|
||||
categoria: _categoria,
|
||||
impostores: impostores,
|
||||
);
|
||||
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const PantallaVerPalabra()),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> _pedirNombreHost() async {
|
||||
final controller = TextEditingController();
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: Text(l10n.yourName),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
textCapitalization: TextCapitalization.words,
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.yourName,
|
||||
prefixIcon: const Icon(Icons.person),
|
||||
),
|
||||
onSubmitted: (v) => Navigator.pop(ctx, v),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx, controller.text),
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controladorNombre.dispose();
|
||||
@@ -316,7 +439,7 @@ class _PantallaCrearPartidaState extends State<PantallaCrearPartida> {
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _jugadores.length >= 3 ? _iniciarPartida : null,
|
||||
onPressed: (_modoMultimovil || _jugadores.length >= 3) ? _iniciarPartida : null,
|
||||
icon: const Icon(Icons.play_arrow),
|
||||
label: Text(l10n.startGame),
|
||||
style: ElevatedButton.styleFrom(
|
||||
|
||||
Reference in New Issue
Block a user