feat: gps coords added
This commit is contained in:
@@ -1,73 +1,87 @@
|
|||||||
|
// src/services/geolocation_service.dart
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'dart:async'; // ← IMPORTANTE: Para TimeoutException
|
||||||
|
|
||||||
class GeolocationService {
|
class GeolocationService {
|
||||||
// Verificar y solicitar permisos de ubicación
|
// Verificar servicios de ubicación
|
||||||
static Future<bool> requestPermission() async {
|
static Future<bool> isLocationServiceEnabled() async {
|
||||||
final status = await Permission.location.request();
|
return await Geolocator.isLocationServiceEnabled();
|
||||||
return status.isGranted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener ubicación actual
|
// Verificar permiso
|
||||||
|
static Future<LocationPermission> checkPermission() async {
|
||||||
|
return await Geolocator.checkPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solicitar permiso
|
||||||
|
static Future<LocationPermission> requestPermission() async {
|
||||||
|
return await Geolocator.requestPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si hay permiso concedido
|
||||||
|
static Future<bool> hasPermission() async {
|
||||||
|
LocationPermission permission = await Geolocator.checkPermission();
|
||||||
|
return permission == LocationPermission.always ||
|
||||||
|
permission == LocationPermission.whileInUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener ubicación actual con timeout
|
||||||
static Future<Position?> getCurrentLocation() async {
|
static Future<Position?> getCurrentLocation() async {
|
||||||
try {
|
try {
|
||||||
// Verificar si los servicios de ubicación están activados
|
// Verificar servicios
|
||||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
if (!serviceEnabled) {
|
if (!serviceEnabled) {
|
||||||
|
print('❌ Servicios de ubicación desactivados');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
print('✅ Servicios de ubicación activados');
|
||||||
|
|
||||||
// Verificar permisos
|
// Verificar permisos
|
||||||
LocationPermission permission = await Geolocator.checkPermission();
|
LocationPermission permission = await Geolocator.checkPermission();
|
||||||
|
print('📌 Permiso actual: $permission');
|
||||||
|
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
permission = await Geolocator.requestPermission();
|
permission = await Geolocator.requestPermission();
|
||||||
|
print('📌 Permiso solicitado: $permission');
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
|
print('❌ Permiso denegado');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission == LocationPermission.deniedForever) {
|
if (permission == LocationPermission.deniedForever) {
|
||||||
|
print('❌ Permiso denegado permanentemente');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener ubicación
|
// Obtener ubicación
|
||||||
|
print('📍 Obteniendo ubicación...');
|
||||||
|
|
||||||
Position position = await Geolocator.getCurrentPosition(
|
Position position = await Geolocator.getCurrentPosition(
|
||||||
desiredAccuracy: LocationAccuracy.high,
|
desiredAccuracy: LocationAccuracy.best,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
print('✅ Ubicación obtenida: ${position.latitude}, ${position.longitude}');
|
||||||
return position;
|
return position;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error getting location: $e');
|
print('❌ Error general: $e');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mostrar diálogo de permisos
|
// Obtener ubicación con reintentos (sin timeout que da problemas)
|
||||||
static Future<bool> showPermissionDialog(BuildContext context) async {
|
static Future<Position?> getCurrentLocationWithRetry({int maxRetries = 3}) async {
|
||||||
final result = await showDialog<bool>(
|
for (int i = 0; i < maxRetries; i++) {
|
||||||
context: context,
|
print('🔄 Intento ${i + 1} de $maxRetries');
|
||||||
barrierDismissible: false,
|
final position = await getCurrentLocation();
|
||||||
builder: (context) => AlertDialog(
|
if (position != null) {
|
||||||
title: const Text('Permiso de ubicación'),
|
return position;
|
||||||
content: const Text(
|
|
||||||
'Necesitamos acceder a tu ubicación para asignar tu domicilio a la ruta correcta de recolección.',
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, false),
|
|
||||||
child: const Text('Cancelar'),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, true),
|
|
||||||
child: const Text('Aceptar', style: TextStyle(color: colorAzul)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result == true) {
|
|
||||||
return await requestPermission();
|
|
||||||
}
|
}
|
||||||
return false;
|
if (i < maxRetries - 1) {
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print('❌ No se pudo obtener ubicación después de $maxRetries intentos');
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
|
// domicilios.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'rutas.dart';
|
import 'rutas.dart';
|
||||||
import '../models/domicilio_model.dart';
|
import '../models/domicilio_model.dart';
|
||||||
import '../services/geolocation_service.dart';
|
import '../services/geolocation_service.dart';
|
||||||
|
import 'nav_bar.dart';
|
||||||
|
import 'main_screen.dart';
|
||||||
|
|
||||||
class DomiciliosView extends StatefulWidget {
|
class DomiciliosView extends StatefulWidget {
|
||||||
const DomiciliosView({super.key});
|
const DomiciliosView({super.key});
|
||||||
@@ -15,12 +18,8 @@ class DomiciliosView extends StatefulWidget {
|
|||||||
class _DomiciliosViewState extends State<DomiciliosView> {
|
class _DomiciliosViewState extends State<DomiciliosView> {
|
||||||
List<Domicilio> domicilios = [];
|
List<Domicilio> domicilios = [];
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
|
|
||||||
// Ubicación actual
|
|
||||||
Position? _currentPosition;
|
|
||||||
bool _isLoadingLocation = false;
|
bool _isLoadingLocation = false;
|
||||||
|
|
||||||
// Controladores para el formulario
|
|
||||||
final TextEditingController nombreController = TextEditingController();
|
final TextEditingController nombreController = TextEditingController();
|
||||||
final TextEditingController coloniaController = TextEditingController();
|
final TextEditingController coloniaController = TextEditingController();
|
||||||
final TextEditingController calleController = TextEditingController();
|
final TextEditingController calleController = TextEditingController();
|
||||||
@@ -69,51 +68,126 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener ubicación actual y mostrar diálogo
|
Future<bool> _showLocationPermissionDialog() async {
|
||||||
|
if (await GeolocationService.hasPermission()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Permiso de ubicación'),
|
||||||
|
content: const Text(
|
||||||
|
'Necesitamos acceder a tu ubicación para asignar tu domicilio a la ruta correcta.',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, false),
|
||||||
|
child: const Text('Cancelar'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, true),
|
||||||
|
child: const Text('Aceptar', style: TextStyle(color: colorAzul)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == true) {
|
||||||
|
LocationPermission newPermission = await GeolocationService.requestPermission();
|
||||||
|
return newPermission == LocationPermission.always ||
|
||||||
|
newPermission == LocationPermission.whileInUse;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⬇️⬇️⬇️ ESTE ES EL MÉTODO QUE DEBES TENER ⬇️⬇️⬇️
|
||||||
Future<void> _obtenerUbicacionYAgregar() async {
|
Future<void> _obtenerUbicacionYAgregar() async {
|
||||||
|
print('🔵 Iniciando obtención de ubicación');
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingLocation = true;
|
_isLoadingLocation = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Solicitar permisos
|
// Verificar GPS
|
||||||
final hasPermission = await GeolocationService.showPermissionDialog(context);
|
final serviceEnabled = await GeolocationService.isLocationServiceEnabled();
|
||||||
|
print('📡 GPS activado: $serviceEnabled');
|
||||||
|
|
||||||
|
if (!serviceEnabled) {
|
||||||
|
setState(() {
|
||||||
|
_isLoadingLocation = false;
|
||||||
|
});
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Activa el GPS para agregar un domicilio'),
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
duration: Duration(seconds: 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar permisos
|
||||||
|
final hasPermission = await _showLocationPermissionDialog();
|
||||||
|
print('🔐 Permiso concedido: $hasPermission');
|
||||||
|
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingLocation = false;
|
_isLoadingLocation = false;
|
||||||
});
|
});
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('Necesitamos tu ubicación para agregar un domicilio'),
|
content: Text('Necesitamos tu ubicación para continuar'),
|
||||||
backgroundColor: Colors.orange,
|
backgroundColor: Colors.orange,
|
||||||
|
duration: Duration(seconds: 3),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtener ubicación
|
// Mostrar mensaje de "obteniendo ubicación"
|
||||||
final position = await GeolocationService.getCurrentLocation();
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text('Obteniendo tu ubicación...'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
backgroundColor: colorAzul,
|
||||||
|
duration: Duration(seconds: 5),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Obtener ubicación con reintentos
|
||||||
|
final position = await GeolocationService.getCurrentLocationWithRetry(maxRetries: 3);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingLocation = false;
|
_isLoadingLocation = false;
|
||||||
_currentPosition = position;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (position == null) {
|
if (position == null) {
|
||||||
|
print('❌ No se pudo obtener ubicación después de reintentos');
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('No se pudo obtener tu ubicación. Activa el GPS.'),
|
content: Text('No se pudo obtener tu ubicación. Revisa tu GPS.'),
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: Colors.red,
|
||||||
|
duration: Duration(seconds: 4),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mostrar diálogo con la ubicación obtenida
|
print('✅ Ubicación obtenida exitosamente: ${position.latitude}, ${position.longitude}');
|
||||||
_mostrarDialogoAgregarConUbicacion(position);
|
_mostrarDialogoAgregarConUbicacion(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _mostrarDialogoAgregarConUbicacion(Position position) {
|
void _mostrarDialogoAgregarConUbicacion(Position position) {
|
||||||
// Limpiar controladores
|
|
||||||
nombreController.clear();
|
nombreController.clear();
|
||||||
coloniaController.clear();
|
coloniaController.clear();
|
||||||
calleController.clear();
|
calleController.clear();
|
||||||
@@ -130,23 +204,16 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
// Título
|
|
||||||
const Text(
|
const Text(
|
||||||
'Añadir domicilio',
|
'Añadir domicilio',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: colorAzul),
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: colorAzul,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
// Mostrar ubicación obtenida
|
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.green.withOpacity(0.1),
|
color: Colors.green.withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(color: Colors.green.withOpacity(0.3)),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -156,22 +223,10 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text('📍 Ubicación obtenida',
|
||||||
'📍 Ubicación obtenida',
|
style: TextStyle(fontSize: 12, color: Colors.green[700])),
|
||||||
style: TextStyle(
|
Text('Lat: ${position.latitude.toStringAsFixed(6)}'),
|
||||||
fontSize: 12,
|
Text('Lng: ${position.longitude.toStringAsFixed(6)}'),
|
||||||
color: Colors.green[700],
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Lat: ${position.latitude.toStringAsFixed(6)}',
|
|
||||||
style: const TextStyle(fontSize: 11),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Lng: ${position.longitude.toStringAsFixed(6)}',
|
|
||||||
style: const TextStyle(fontSize: 11),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -179,36 +234,14 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
// Campo: Nombre del domicilio
|
_buildCampoTexto(controller: nombreController, hint: 'Nombre del domicilio', icon: Icons.home_outlined),
|
||||||
_buildCampoTexto(
|
|
||||||
controller: nombreController,
|
|
||||||
hint: 'Nombre del domicilio',
|
|
||||||
icon: Icons.home_outlined,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
// Campo: Colonia
|
_buildCampoTexto(controller: coloniaController, hint: 'Colonia', icon: Icons.location_city_outlined),
|
||||||
_buildCampoTexto(
|
|
||||||
controller: coloniaController,
|
|
||||||
hint: 'Colonia',
|
|
||||||
icon: Icons.location_city_outlined,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
// Campo: Calle
|
_buildCampoTexto(controller: calleController, hint: 'Calle', icon: Icons.streetview),
|
||||||
_buildCampoTexto(
|
|
||||||
controller: calleController,
|
|
||||||
hint: 'Calle',
|
|
||||||
icon: Icons.streetview,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
// Campo: Número
|
_buildCampoTexto(controller: numeroController, hint: 'Número', icon: Icons.numbers, keyboardType: TextInputType.number),
|
||||||
_buildCampoTexto(
|
|
||||||
controller: numeroController,
|
|
||||||
hint: 'Número',
|
|
||||||
icon: Icons.numbers,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
// Botones
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
@@ -216,18 +249,10 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
side: BorderSide(color: colorAzul, width: 2),
|
side: BorderSide(color: colorAzul, width: 2),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
'Cancelar',
|
|
||||||
style: TextStyle(fontSize: 16, color: colorAzul),
|
|
||||||
),
|
),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Cancelar', style: TextStyle(color: colorAzul)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
@@ -235,21 +260,10 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: colorAzul,
|
backgroundColor: colorAzul,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
_agregarDomicilio(
|
|
||||||
latitud: position.latitude,
|
|
||||||
longitud: position.longitude,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
'Agregar',
|
|
||||||
style: TextStyle(fontSize: 16, color: Colors.white),
|
|
||||||
),
|
),
|
||||||
|
onPressed: () => _agregarDomicilio(latitud: position.latitude, longitud: position.longitude),
|
||||||
|
child: const Text('Agregar', style: TextStyle(color: Colors.white)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -274,10 +288,7 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: hint,
|
hintText: hint,
|
||||||
prefixIcon: Icon(icon, color: colorAzul),
|
prefixIcon: Icon(icon, color: colorAzul),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(15)),
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
borderSide: const BorderSide(color: colorAzul),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
borderSide: BorderSide(color: colorAzul.withOpacity(0.5)),
|
borderSide: BorderSide(color: colorAzul.withOpacity(0.5)),
|
||||||
@@ -290,28 +301,23 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _agregarDomicilio({required double latitud, required double longitud}) {
|
void _agregarDomicilio({required double latitud, required double longitud}) async {
|
||||||
// Validar campos
|
if (nombreController.text.trim().isEmpty ||
|
||||||
if (nombreController.text.isEmpty ||
|
coloniaController.text.trim().isEmpty ||
|
||||||
coloniaController.text.isEmpty ||
|
calleController.text.trim().isEmpty ||
|
||||||
calleController.text.isEmpty ||
|
numeroController.text.trim().isEmpty) {
|
||||||
numeroController.text.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(content: Text('Llena todos los campos'), backgroundColor: Colors.red),
|
||||||
content: Text('Por favor, llena todos los campos'),
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crear nuevo domicilio con lat/lng
|
|
||||||
final nuevoDomicilio = Domicilio(
|
final nuevoDomicilio = Domicilio(
|
||||||
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
nombre: nombreController.text,
|
nombre: nombreController.text.trim(),
|
||||||
colonia: coloniaController.text,
|
colonia: coloniaController.text.trim(),
|
||||||
calle: calleController.text,
|
calle: calleController.text.trim(),
|
||||||
numero: numeroController.text,
|
numero: numeroController.text.trim(),
|
||||||
latitud: latitud,
|
latitud: latitud,
|
||||||
longitud: longitud,
|
longitud: longitud,
|
||||||
);
|
);
|
||||||
@@ -320,58 +326,49 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
domicilios.add(nuevoDomicilio);
|
domicilios.add(nuevoDomicilio);
|
||||||
});
|
});
|
||||||
|
|
||||||
_guardarDomicilios();
|
await _guardarDomicilios();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(content: Text('Domicilio agregado'), backgroundColor: colorAzul, duration: const Duration(seconds: 2)),
|
||||||
content: Text('Domicilio "${nombreController.text}" agregado'),
|
|
||||||
backgroundColor: colorAzul,
|
|
||||||
duration: const Duration(seconds: 2),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _eliminarDomicilio(int index) async {
|
void _eliminarDomicilio(int index) async {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) => AlertDialog(
|
||||||
return AlertDialog(
|
|
||||||
title: const Text('Eliminar domicilio'),
|
title: const Text('Eliminar domicilio'),
|
||||||
content: Text('¿Deseas eliminar "${domicilios[index].nombre}"?'),
|
content: Text('¿Eliminar "${domicilios[index].nombre}"?'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancelar')),
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: const Text('Cancelar'),
|
|
||||||
),
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
setState(() {
|
setState(() => domicilios.removeAt(index));
|
||||||
domicilios.removeAt(index);
|
|
||||||
});
|
|
||||||
await _guardarDomicilios();
|
await _guardarDomicilios();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(
|
|
||||||
content: Text('Domicilio eliminado'),
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
duration: Duration(seconds: 2),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: const Text('Eliminar', style: TextStyle(color: Colors.red)),
|
child: const Text('Eliminar', style: TextStyle(color: Colors.red)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
void _navegarA(int index) {
|
||||||
|
if (index == 1) {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => const MainScreen()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Scaffold(
|
||||||
color: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
child: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
@@ -379,20 +376,9 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: colorAzul,
|
color: colorAzul,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
|
||||||
bottomLeft: Radius.circular(20),
|
|
||||||
bottomRight: Radius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Domicilios',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 28,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
|
child: const Text('Domicilios', style: TextStyle(color: Colors.white, fontSize: 28, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _isLoading
|
child: _isLoading
|
||||||
@@ -402,27 +388,11 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Icons.home_outlined, size: 100, color: Colors.grey.withOpacity(0.5)),
|
||||||
Icons.home_outlined,
|
|
||||||
size: 100,
|
|
||||||
color: Colors.grey.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Text(
|
Text('No hay domicilios', style: TextStyle(fontSize: 18, color: Colors.grey.withOpacity(0.7))),
|
||||||
'No hay domicilios agregados',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
color: Colors.grey.withOpacity(0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Text(
|
Text('Toca el botón + para agregar', style: TextStyle(fontSize: 14, color: Colors.grey.withOpacity(0.5))),
|
||||||
'Toca el botón + para agregar',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.grey.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -430,7 +400,7 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
itemCount: domicilios.length,
|
itemCount: domicilios.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final domicilio = domicilios[index];
|
final d = domicilios[index];
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 20),
|
padding: const EdgeInsets.only(bottom: 20),
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -447,36 +417,13 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(d.nombre, style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
|
||||||
domicilio.nombre,
|
Text(d.direccionCompleta, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
style: const TextStyle(
|
Text('📍 ${d.latitud.toStringAsFixed(4)}, ${d.longitud.toStringAsFixed(4)}', style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
domicilio.direccionCompleta,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Mostrar lat/lng (para debug)
|
|
||||||
Text(
|
|
||||||
'📍 ${domicilio.latitud.toStringAsFixed(4)}, ${domicilio.longitud.toStringAsFixed(4)}',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(onPressed: () => _eliminarDomicilio(index), icon: const Icon(Icons.delete_outline, size: 40), color: Colors.red),
|
||||||
onPressed: () => _eliminarDomicilio(index),
|
|
||||||
icon: const Icon(Icons.delete_outline, size: 40),
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -487,33 +434,16 @@ class _DomiciliosViewState extends State<DomiciliosView> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
child: _isLoadingLocation
|
child: _isLoadingLocation
|
||||||
? Container(
|
? Container(width: double.infinity, height: 100, decoration: BoxDecoration(color: colorAzul, borderRadius: BorderRadius.circular(20)), child: const Center(child: CircularProgressIndicator(color: Colors.white)))
|
||||||
width: double.infinity,
|
|
||||||
height: 100,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorAzul,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: const Center(
|
|
||||||
child: CircularProgressIndicator(color: Colors.white),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: GestureDetector(
|
: GestureDetector(
|
||||||
onTap: _obtenerUbicacionYAgregar,
|
onTap: _obtenerUbicacionYAgregar,
|
||||||
child: Container(
|
child: Container(width: double.infinity, height: 100, decoration: BoxDecoration(color: colorAzul, borderRadius: BorderRadius.circular(20)), child: const Icon(Icons.add, color: Colors.white, size: 80)),
|
||||||
width: double.infinity,
|
|
||||||
height: 100,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorAzul,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: const Icon(Icons.add, color: Colors.white, size: 80),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: CustomNavBar(currentIndex: 0, onTap: _navegarA),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user