Files
hackathon-heavy-gears-7cc00…/lib/ruta_exclusiva.dart
2026-05-23 09:30:42 -06:00

398 lines
13 KiB
Dart

import 'package:flutter/material.dart';
import 'camion_estado.dart';
// ─── DATOS DE COLONIAS (de tu colonias-rutas.json) ──────────────
const List<Map<String, String>> _coloniasRutas = [
{'colonia': 'Zona Centro', 'routeId': 'RUTA-01', 'horario': 'Matutino (06:30 - 07:15)'},
{'colonia': 'Las Arboledas', 'routeId': 'RUTA-01', 'horario': 'Matutino (07:00 - 07:30)'},
{'colonia': 'Trojes', 'routeId': 'RUTA-13', 'horario': 'Matutino (06:40 - 07:10)'},
{'colonia': 'San Juanico', 'routeId': 'RUTA-03', 'horario': 'Matutino (06:45 - 07:15)'},
{'colonia': 'Los Olivos', 'routeId': 'RUTA-04', 'horario': 'Matutino (07:00 - 07:40)'},
{'colonia': 'Rancho Seco', 'routeId': 'RUTA-05', 'horario': 'Vespertino (14:15 - 15:00)'},
{'colonia': 'Las Insurgentes', 'routeId': 'RUTA-12', 'horario': 'Matutino (06:35 - 07:10)'},
];
// ════════════════════════════════════════════════════════════════
// PANTALLA RUTA EXCLUSIVA
// ════════════════════════════════════════════════════════════════
class RutaExclusivaScreen extends StatefulWidget {
final String direccionUsuario; // ← viene desde domicilios.dart
final String coloniaUsuario; // ← viene desde domicilios.dart
const RutaExclusivaScreen({
super.key,
required this.direccionUsuario,
required this.coloniaUsuario,
});
@override
State<RutaExclusivaScreen> createState() => _RutaExclusivaScreenState();
}
class _RutaExclusivaScreenState extends State<RutaExclusivaScreen> {
Map<String, String>? _rutaAsignada; // solo la ruta del usuario
bool _accesoValidado = false;
bool _cargando = true;
@override
void initState() {
super.initState();
camionEstado.addListener(_actualizar);
_validarAcceso();
}
void _actualizar() {
if (mounted) setState(() {});
}
@override
void dispose() {
camionEstado.removeListener(_actualizar);
super.dispose();
}
// ── Valida que la colonia del usuario exista en las rutas ────
void _validarAcceso() async {
await Future.delayed(const Duration(milliseconds: 800)); // simula carga
final coloniaLower = widget.coloniaUsuario.trim().toLowerCase();
final encontrada = _coloniasRutas.firstWhere(
(r) => r['colonia']!.toLowerCase() == coloniaLower,
orElse: () => {},
);
setState(() {
_cargando = false;
if (encontrada.isNotEmpty) {
_rutaAsignada = encontrada;
_accesoValidado = true;
} else {
_accesoValidado = false;
}
});
}
// ── Color según etapa del camión ─────────────────────────────
Color _colorEtapa() {
final id = camionEstado.positionId;
if (id <= 1) return Colors.grey;
if (id <= 3) return Colors.blue;
if (id <= 5) return Colors.orange;
if (id == 6) return Colors.green;
return Colors.grey;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: const Text('Mi Ruta Asignada'),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
body: _cargando
? _buildCargando()
: _accesoValidado
? _buildRutaValidada()
: _buildSinAcceso(),
);
}
// ── Pantalla de carga ────────────────────────────────────────
Widget _buildCargando() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(color: Colors.green),
SizedBox(height: 16),
Text(
'Validando tu dirección...',
style: TextStyle(color: Colors.grey, fontSize: 14),
),
],
),
);
}
// ── Sin acceso: colonia no registrada ────────────────────────
Widget _buildSinAcceso() {
return Center(
child: Padding(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.lock_outline, size: 80, color: Colors.red[300]),
const SizedBox(height: 20),
const Text(
'Dirección no reconocida',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(
'Tu colonia "${widget.coloniaUsuario}" no está registrada en el sistema de rutas. Actualiza tu domicilio o contacta al municipio.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const SizedBox(height: 28),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.arrow_back),
label: const Text('Actualizar domicilio'),
),
],
),
),
);
}
// ── Ruta validada: solo info de SU ruta ──────────────────────
Widget _buildRutaValidada() {
final ruta = _rutaAsignada!;
final etapa = CamionEstado.etapas[camionEstado.positionId] ?? '';
final etaInfo = CamionEstado.etaInfo[camionEstado.positionId]!;
final color = _colorEtapa();
final minutos = etaInfo['minutos'] as int;
return ListView(
padding: const EdgeInsets.all(16),
children: [
// ── BADGE DE ACCESO VALIDADO ──────────────────────
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
decoration: BoxDecoration(
color: Colors.green[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.green),
),
child: Row(
children: [
const Icon(Icons.verified_user, color: Colors.green),
const SizedBox(width: 10),
Expanded(
child: Text(
'Acceso validado para ${widget.coloniaUsuario}. Solo ves la información de tu ruta.',
style: const TextStyle(
fontSize: 13,
color: Colors.green,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
const SizedBox(height: 16),
// ── CARD: DATOS DE LA RUTA ────────────────────────
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.route, color: Colors.green, size: 28),
const SizedBox(width: 10),
Text(
ruta['routeId']!,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
const SizedBox(height: 16),
_filaInfo(Icons.location_on, 'Colonia', ruta['colonia']!),
_filaInfo(Icons.schedule, 'Horario', ruta['horario']!),
_filaInfo(Icons.home, 'Dirección', widget.direccionUsuario),
],
),
),
),
const SizedBox(height: 16),
// ── CARD: ESTADO EN TIEMPO REAL ───────────────────
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.local_shipping, color: Colors.green, size: 28),
SizedBox(width: 10),
Text(
'Estado en tiempo real',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
// Barra de progreso
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: LinearProgressIndicator(
value: camionEstado.positionId / 8,
minHeight: 12,
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(color),
),
),
const SizedBox(height: 12),
// Etapa actual
Container(
width: double.infinity,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: color.withValues(alpha:0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withValues(alpha:0.3)),
),
child: Text(
etapa,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: color,
),
),
),
const SizedBox(height: 12),
// ETA
if (minutos > 0)
Container(
width: double.infinity,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.green[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
const Icon(Icons.timer, color: Colors.green),
const SizedBox(width: 10),
Text(
'Llega en aproximadamente $minutos minutos',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
),
],
),
),
),
const SizedBox(height: 16),
// ── AVISO PRIVACIDAD ──────────────────────────────
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.blue.shade200),
),
child: const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.shield, color: Colors.blue),
SizedBox(width: 10),
Expanded(
child: Text(
'Visión de túnel activa: No puedes ver rutas, horarios ni ubicaciones de otras colonias.',
style: TextStyle(fontSize: 13, color: Colors.blue),
),
),
],
),
),
],
);
}
// ── Widget auxiliar para filas de info ───────────────────────
Widget _filaInfo(IconData icono, String label, String valor) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icono, color: Colors.green, size: 20),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
Text(
valor,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
);
}
}