modificaciones en el sistema
This commit is contained in:
398
lib/ruta_exclusiva.dart
Normal file
398
lib/ruta_exclusiva.dart
Normal file
@@ -0,0 +1,398 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user