feat: map deploy logic

This commit is contained in:
imlildud
2026-05-23 00:05:28 -06:00
parent e10bb97afb
commit 1f2d162c34
2 changed files with 287 additions and 35 deletions

View File

@@ -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<Map<String, String>> 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<HorariosView> createState() => _HorariosViewState();
}
class _HorariosViewState extends State<HorariosView> {
List<Domicilio> domicilios = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_cargarDomicilios();
}
Future<void> _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];
}
}

View File

@@ -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<MapaExpandible> createState() => _MapaExpandibleState();
}
class _MapaExpandibleState extends State<MapaExpandible> {
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,
),
),
],
),
],
),
),
),
),
],
);
}
}