import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'package:geosector_app/core/constants/app_keys.dart'; import 'package:geosector_app/core/services/api_service.dart'; // Import du service singleton /// Widget de carte réutilisable utilisant Mapbox /// /// Ce widget encapsule un FlutterMap avec des tuiles Mapbox et fournit /// des fonctionnalités pour afficher des marqueurs, des polygones et des contrôles. class MapboxMap extends StatefulWidget { /// Position initiale de la carte final LatLng initialPosition; /// Niveau de zoom initial final double initialZoom; /// Liste des marqueurs à afficher final List? markers; /// Liste des polygones à afficher final List? polygons; /// Contrôleur de carte externe (optionnel) final MapController? mapController; /// Callback appelé lorsque la carte est déplacée final void Function(MapEvent)? onMapEvent; /// Afficher les boutons de contrôle (zoom, localisation) final bool showControls; /// Style de la carte Mapbox (optionnel) /// Si non spécifié, utilise le style par défaut 'mapbox/streets-v12' final String? mapStyle; const MapboxMap({ super.key, this.initialPosition = const LatLng(48.1173, -1.6778), // Rennes par défaut this.initialZoom = 13.0, this.markers, this.polygons, this.mapController, this.onMapEvent, this.showControls = true, this.mapStyle, }); @override State createState() => _MapboxMapState(); } class _MapboxMapState extends State { /// Contrôleur de carte interne late final MapController _mapController; /// Niveau de zoom actuel double _currentZoom = 13.0; @override void initState() { super.initState(); _mapController = widget.mapController ?? MapController(); _currentZoom = widget.initialZoom; } @override void dispose() { // Ne pas disposer le contrôleur s'il a été fourni de l'extérieur if (widget.mapController == null) { _mapController.dispose(); } super.dispose(); } /// Construit un bouton de contrôle de carte Widget _buildMapButton({ required IconData icon, required VoidCallback onPressed, }) { return Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 6, offset: const Offset(0, 3), ), ], ), child: IconButton( icon: Icon(icon, size: 20), onPressed: onPressed, padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ); } @override Widget build(BuildContext context) { // Déterminer l'URL du template de tuiles Mapbox // Utiliser l'environnement actuel pour obtenir la bonne clé API final String environment = ApiService.instance.getCurrentEnvironment(); final String mapboxToken = AppKeys.getMapboxApiKey(environment); final String mapStyle = widget.mapStyle ?? 'mapbox/streets-v11'; final String urlTemplate = 'https://api.mapbox.com/styles/v1/$mapStyle/tiles/256/{z}/{x}/{y}@2x?access_token=$mapboxToken'; return Stack( children: [ // Carte principale FlutterMap( mapController: _mapController, options: MapOptions( initialCenter: widget.initialPosition, initialZoom: widget.initialZoom, onMapEvent: (event) { if (event is MapEventMove) { setState(() { // Dans flutter_map 8.1.1, nous devons utiliser le contrôleur pour obtenir le zoom actuel _currentZoom = _mapController.camera.zoom; }); } // Appeler le callback externe si fourni if (widget.onMapEvent != null) { widget.onMapEvent!(event); } }, ), children: [ // Tuiles de la carte (Mapbox) TileLayer( urlTemplate: urlTemplate, userAgentPackageName: 'app.geosector.fr', maxNativeZoom: 19, additionalOptions: { 'accessToken': mapboxToken, }, ), // Polygones if (widget.polygons != null && widget.polygons!.isNotEmpty) PolygonLayer(polygons: widget.polygons!), // Marqueurs if (widget.markers != null && widget.markers!.isNotEmpty) MarkerLayer(markers: widget.markers!), ], ), // Boutons de contrôle if (widget.showControls) Positioned( bottom: 16, right: 16, child: Column( children: [ // Bouton de zoom + _buildMapButton( icon: Icons.add, onPressed: () { _mapController.move( _mapController.camera.center, _mapController.camera.zoom + 1, ); }, ), const SizedBox(height: 8), // Bouton de zoom - _buildMapButton( icon: Icons.remove, onPressed: () { _mapController.move( _mapController.camera.center, _mapController.camera.zoom - 1, ); }, ), const SizedBox(height: 8), // Bouton de localisation _buildMapButton( icon: Icons.my_location, onPressed: () { _mapController.move( widget.initialPosition, 15, ); }, ), ], ), ), ], ); } }