From 1f2d162c343dfa27c0b8e5f76d67eaef79a2e693 Mon Sep 17 00:00:00 2001 From: imlildud Date: Sat, 23 May 2026 00:05:28 -0600 Subject: [PATCH] feat: map deploy logic --- lib/src/views/horarios.dart | 216 ++++++++++++++++++++++++----- lib/src/views/mapa_expandible.dart | 106 ++++++++++++++ 2 files changed, 287 insertions(+), 35 deletions(-) create mode 100644 lib/src/views/mapa_expandible.dart diff --git a/lib/src/views/horarios.dart b/lib/src/views/horarios.dart index 1886447..5a4a36f 100644 --- a/lib/src/views/horarios.dart +++ b/lib/src/views/horarios.dart @@ -1,20 +1,46 @@ import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'rutas.dart'; +import '../models/domicilio_model.dart'; +import 'mapa_expandible.dart'; -class HorariosView extends StatelessWidget { +class HorariosView extends StatefulWidget { const HorariosView({super.key}); @override - Widget build(BuildContext context) { - final List> rutas = List.generate( - 6, - (index) => { - 'colonia': 'Colonia ${index + 1}', - 'ruta': 'Ruta ${index + 1}', - 'horario': '${8 + (index % 3)}:${30 * (index % 2)} ${index < 3 ? 'AM' : 'PM'}', - }, - ); + State createState() => _HorariosViewState(); +} +class _HorariosViewState extends State { + List domicilios = []; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _cargarDomicilios(); + } + + Future _cargarDomicilios() async { + try { + final prefs = await SharedPreferences.getInstance(); + final String? domiciliosString = prefs.getString('domicilios'); + + setState(() { + if (domiciliosString != null && domiciliosString.isNotEmpty) { + domicilios = Domicilio.decode(domiciliosString); + } + _isLoading = false; + }); + } catch (e) { + setState(() { + _isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { return Container( color: Colors.white, child: SafeArea( @@ -32,7 +58,7 @@ class HorariosView extends StatelessWidget { ), ), child: const Text( - 'Horarios', + 'Mis Rutas', style: TextStyle( color: Colors.white, fontSize: 32, @@ -41,32 +67,46 @@ class HorariosView extends StatelessWidget { textAlign: TextAlign.center, ), ), - // Lista de horarios + // Lista de domicilios Expanded( - child: ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 24), - itemCount: rutas.length, - itemBuilder: (context, index) { - final item = rutas[index]; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(item['colonia']!, - style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)), - Text(item['ruta']!, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), - ], - ), - Text(item['horario']!, - style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), - ], + child: _isLoading + ? const Center(child: CircularProgressIndicator(color: colorAzul)) + : domicilios.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.location_on_outlined, + size: 100, + color: Colors.grey.withOpacity(0.5), ), - ); + const SizedBox(height: 20), + Text( + 'No hay domicilios agregados', + style: TextStyle( + fontSize: 18, + color: Colors.grey.withOpacity(0.7), + ), + ), + const SizedBox(height: 10), + Text( + 'Agrega domicilios desde la pestaña Domicilios', + style: TextStyle( + fontSize: 14, + color: Colors.grey.withOpacity(0.5), + ), + textAlign: TextAlign.center, + ), + ], + ), + ) + : ListView.builder( + padding: const EdgeInsets.all(20), + itemCount: domicilios.length, + itemBuilder: (context, index) { + final domicilio = domicilios[index]; + return _buildDomicilioCard(domicilio, index); }, ), ), @@ -75,4 +115,110 @@ class HorariosView extends StatelessWidget { ), ); } + + Widget _buildDomicilioCard(Domicilio domicilio, int index) { + return Padding( + padding: const EdgeInsets.only(bottom: 20), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.black, width: 4), + borderRadius: BorderRadius.circular(25), + ), + child: Column( + children: [ + // Contenido principal del domicilio + Padding( + padding: const EdgeInsets.all(15), + child: Column( + children: [ + // Info principal + Row( + children: [ + const Icon(Icons.home_outlined, size: 60), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + domicilio.nombre, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + domicilio.direccionCompleta, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 4), + Text( + '📍 ${domicilio.latitud.toStringAsFixed(4)}, ${domicilio.longitud.toStringAsFixed(4)}', + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 12), + // Fila de horario y botón de mapa + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Horario (placeholder para API del backend) + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + decoration: BoxDecoration( + color: colorAzul.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon(Icons.access_time, color: colorAzul, size: 20), + const SizedBox(width: 8), + Text( + 'Horario: ${_obtenerHorarioEstimado(index)}', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: colorAzul, + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + // Botón/flecha para mapa desplegable + MapaExpandible( + latitud: domicilio.latitud, + longitud: domicilio.longitud, + nombreDomicilio: domicilio.nombre, + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } + + // Horario estimado (placeholder para cuando conectes con el backend) + String _obtenerHorarioEstimado(int index) { + // Esto es solo un placeholder - aquí irá la llamada a tu API + final horarios = ['8:00 AM - 9:00 AM', '9:30 AM - 10:30 AM', '11:00 AM - 12:00 PM', '1:00 PM - 2:00 PM', '3:00 PM - 4:00 PM', '5:00 PM - 6:00 PM']; + return horarios[index % horarios.length]; + } } \ No newline at end of file diff --git a/lib/src/views/mapa_expandible.dart b/lib/src/views/mapa_expandible.dart new file mode 100644 index 0000000..190a1da --- /dev/null +++ b/lib/src/views/mapa_expandible.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; + +class MapaExpandible extends StatefulWidget { + final double latitud; + final double longitud; + final String nombreDomicilio; + + const MapaExpandible({ + super.key, + required this.latitud, + required this.longitud, + required this.nombreDomicilio, + }); + + @override + State createState() => _MapaExpandibleState(); +} + +class _MapaExpandibleState extends State { + bool _isExpanded = false; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + // Botón para expandir/colapsar + GestureDetector( + onTap: () { + setState(() { + _isExpanded = !_isExpanded; + }); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + _isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, + color: colorAzul, + size: 20, + ), + const SizedBox(width: 4), + Text( + _isExpanded ? 'Ocultar mapa' : 'Ver mapa', + style: TextStyle( + color: colorAzul, + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + // Mapa (visible solo cuando está expandido) + if (_isExpanded) + Padding( + padding: const EdgeInsets.only(top: 12), + child: Container( + height: 250, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + border: Border.all(color: colorAzul.withOpacity(0.3), width: 1), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(15), + child: FlutterMap( + options: MapOptions( + initialCenter: LatLng(widget.latitud, widget.longitud), + initialZoom: 15, + ), + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'com.example.app', + ), + MarkerLayer( + markers: [ + Marker( + point: LatLng(widget.latitud, widget.longitud), + width: 40, + height: 40, + child: const Icon( + Icons.location_pin, + color: Colors.red, + size: 40, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ], + ); + } +} \ No newline at end of file