import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:http/http.dart' as http; import 'package:prosapp_web_app/providers/auth_provider.dart'; import 'package:prosapp_web_app/router/router.dart'; import 'package:prosapp_web_app/services/navigation_service.dart'; import 'package:provider/provider.dart'; class SetupCityView extends StatefulWidget { const SetupCityView({super.key}); @override State createState() => _SetupCityViewState(); } class _SetupCityViewState extends State { final _controller = TextEditingController(); bool _loading = false; bool _detecting = true; String? _error; @override void initState() { super.initState(); _detectCity(); } @override void dispose() { _controller.dispose(); super.dispose(); } Future _detectCity() async { setState(() => _detecting = true); try { // Pedir permiso de ubicación LocationPermission perm = await Geolocator.checkPermission(); if (perm == LocationPermission.denied) { perm = await Geolocator.requestPermission(); } if (perm == LocationPermission.deniedForever || perm == LocationPermission.denied) { setState(() => _detecting = false); return; } final pos = await Geolocator.getCurrentPosition( locationSettings: const LocationSettings( accuracy: LocationAccuracy.low, timeLimit: Duration(seconds: 8), ), ); // Reverse geocode con Nominatim (sin API key) final url = Uri.parse( 'https://nominatim.openstreetmap.org/reverse' '?format=json&lat=${pos.latitude}&lon=${pos.longitude}&zoom=10&addressdetails=1', ); final resp = await http.get(url, headers: {'User-Agent': 'ProsApp/1.0 (prosapp.co)'}); if (resp.statusCode == 200) { final json = jsonDecode(resp.body) as Map; final address = json['address'] as Map?; final city = address?['city'] as String? ?? address?['town'] as String? ?? address?['municipality'] as String? ?? address?['county'] as String?; if (city != null && city.isNotEmpty) { _controller.text = city; } } } catch (_) { // GPS no disponible, el usuario ingresa manualmente } finally { if (mounted) setState(() => _detecting = false); } } Future _save() async { final city = _controller.text.trim(); if (city.isEmpty) { setState(() => _error = 'Ingresa tu ciudad'); return; } setState(() { _loading = true; _error = null; }); try { await context.read().updateCity(city); NavigationService.replaceTo(Flurorouter.dashboardRoute); } catch (_) { setState(() => _loading = false); } } void _skip() => NavigationService.replaceTo(Flurorouter.dashboardRoute); @override Widget build(BuildContext context) { final isDark = Theme.of(context).brightness == Brightness.dark; final textSec = isDark ? Colors.white54 : Colors.grey[600]; return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Ícono Container( width: 72, height: 72, decoration: BoxDecoration( color: const Color(0xFF42A4EF).withOpacity(0.12), shape: BoxShape.circle, ), child: const Icon(Icons.location_city_outlined, color: Color(0xFF42A4EF), size: 34), ), const SizedBox(height: 20), const Text('¿En qué ciudad estás?', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold), textAlign: TextAlign.center), const SizedBox(height: 8), Text( 'Así podemos mostrarte profesionales cerca de ti.', textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: textSec), ), const SizedBox(height: 28), if (_detecting) Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFF42A4EF))), const SizedBox(width: 10), Text('Detectando tu ubicación...', style: TextStyle(fontSize: 13, color: textSec)), ], ), ), // Campo de ciudad TextField( controller: _controller, textCapitalization: TextCapitalization.words, onSubmitted: (_) => _save(), decoration: InputDecoration( labelText: 'Ciudad', hintText: 'Ej: Bucaramanga', prefixIcon: const Icon(Icons.location_on_outlined, color: Color(0xFF42A4EF)), errorText: _error, filled: true, fillColor: isDark ? const Color(0xFF1E293B) : const Color(0xFFF5F8FF), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: isDark ? const Color(0xFF334155) : const Color(0xFFE0E7FF), ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide( color: Color(0xFF42A4EF), width: 2), ), suffixIcon: _detecting ? const Padding( padding: EdgeInsets.all(14), child: SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: Color(0xFF42A4EF)), ), ) : IconButton( icon: const Icon(Icons.my_location, color: Color(0xFF42A4EF)), tooltip: 'Detectar de nuevo', onPressed: _detectCity, ), ), ), const SizedBox(height: 20), // Botón guardar SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _loading ? null : _save, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF42A4EF), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), elevation: 0, ), child: _loading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2)) : const Text('Guardar ciudad', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600)), ), ), const SizedBox(height: 12), // Omitir TextButton( onPressed: _skip, child: Text('Omitir por ahora', style: TextStyle(color: textSec, fontSize: 13)), ), ], ), ), ), ); } }