From 4a2588f6f22b578a242a775045c5a96dfa92c648 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 27 Jun 2026 07:59:40 -0500 Subject: [PATCH] feat: load Google Maps key dynamically from backend - MapsService: obtiene la key via API e inyecta el script Maps JS en runtime - LocationPickerDialog: muestra error descriptivo si la key no esta configurada - index.html: eliminado script estatico de Maps Co-Authored-By: Claude Sonnet 4.6 --- lib/services/maps_service.dart | 60 +++++++++++++++++++++++++++++ lib/ui/widgets/location_picker.dart | 34 +++++++++++++--- web/index.html | 2 - 3 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 lib/services/maps_service.dart diff --git a/lib/services/maps_service.dart b/lib/services/maps_service.dart new file mode 100644 index 0000000..5ec79c3 --- /dev/null +++ b/lib/services/maps_service.dart @@ -0,0 +1,60 @@ +import 'dart:async'; +// ignore: avoid_web_libraries_in_flutter +import 'dart:js' as js; +import 'package:flutter/foundation.dart'; +import 'package:prosapp_web_app/services/api_service.dart'; + +class MapsService { + static bool _loaded = false; + static Completer? _completer; + + static Future load() async { + if (_loaded) return true; + if (_completer != null) return _completer!.future; + + _completer = Completer(); + + try { + final data = await ApiService.instance.get('/settings/maps-key'); + final apiKey = data['api_key'] as String? ?? ''; + + if (apiKey.isEmpty) { + debugPrint('MapsService: API key no configurada'); + _completer!.complete(false); + return false; + } + + if (kIsWeb) { + _injectScript(apiKey); + } + + _loaded = true; + _completer!.complete(true); + return true; + } catch (e) { + debugPrint('MapsService: error cargando key — $e'); + _completer!.complete(false); + return false; + } + } + + static void _injectScript(String apiKey) { + // Verifica si ya está cargado + final existing = js.context.callMethod('eval', [ + 'typeof google !== "undefined" && typeof google.maps !== "undefined"' + ]); + if (existing == true) { + _loaded = true; + return; + } + + js.context.callMethod('eval', [''' + (function() { + var s = document.createElement('script'); + s.src = 'https://maps.googleapis.com/maps/api/js?key=$apiKey'; + s.async = true; + document.head.appendChild(s); + })(); + ''']); + } +} diff --git a/lib/ui/widgets/location_picker.dart b/lib/ui/widgets/location_picker.dart index c6112c2..89bcd83 100644 --- a/lib/ui/widgets/location_picker.dart +++ b/lib/ui/widgets/location_picker.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:http/http.dart' as http; - -const _mapsApiKey = String.fromEnvironment('MAPS_API_KEY', defaultValue: ''); +import 'package:prosapp_web_app/services/api_service.dart'; +import 'package:prosapp_web_app/services/maps_service.dart'; class LocationResult { final LatLng position; @@ -38,10 +38,23 @@ class _LocationPickerDialogState extends State { String _city = ''; bool _loading = true; bool _geocoding = false; + bool _mapsReady = false; + String? _mapsError; @override void initState() { super.initState(); + _initMaps(); + } + + Future _initMaps() async { + final ok = await MapsService.load(); + if (!mounted) return; + if (!ok) { + setState(() { _loading = false; _mapsError = 'Google Maps no configurado. Configura la API Key en el panel de administración.'; }); + return; + } + setState(() => _mapsReady = true); if (widget.initialPosition != null) { _position = widget.initialPosition!; _loading = false; @@ -95,13 +108,15 @@ class _LocationPickerDialogState extends State { } Future _reverseGeocode(LatLng pos) async { - if (_mapsApiKey.isEmpty) return; + final keyData = await ApiService.instance.get('/settings/maps-key').catchError((_) => {}); + final apiKey = keyData['api_key'] as String? ?? ''; + if (apiKey.isEmpty) return; setState(() => _geocoding = true); try { final url = Uri.parse( 'https://maps.googleapis.com/maps/api/geocode/json' '?latlng=${pos.latitude},${pos.longitude}' - '&key=$_mapsApiKey' + '&key=$apiKey' '&language=es', ); final res = await http.get(url); @@ -169,7 +184,16 @@ class _LocationPickerDialogState extends State { // Map Expanded( - child: _loading + child: _mapsError != null + ? Center(child: Padding( + padding: const EdgeInsets.all(24), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + const Icon(Icons.map_outlined, size: 48, color: Colors.grey), + const SizedBox(height: 12), + Text(_mapsError!, textAlign: TextAlign.center, style: const TextStyle(color: Colors.grey)), + ]), + )) + : _loading ? const Center(child: CircularProgressIndicator()) : Stack( children: [ diff --git a/web/index.html b/web/index.html index 09e23b3..7e3dca6 100644 --- a/web/index.html +++ b/web/index.html @@ -16,8 +16,6 @@ - -