Files
geo/flutt/lib/presentation/widgets/mapbox_map.dart

201 lines
5.8 KiB
Dart

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';
/// 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<Marker>? markers;
/// Liste des polygones à afficher
final List<Polygon>? 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<MapboxMap> createState() => _MapboxMapState();
}
class _MapboxMapState extends State<MapboxMap> {
/// 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
final String mapboxToken = AppKeys.mapboxApiKey;
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,
);
},
),
],
),
),
],
);
}
}