Ajouter et supprimer des marqueurs sur une carte Google Maps
Maintenant que vous avez ajouté une carte Google Maps à votre application Flutter, voici comment ajouter des marqueurs dessus pour indiquer des points importants, ou des endroits sauvegardés par l’utilisateur :
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MapScreen(),
);
}
}
class MapScreen extends StatefulWidget {
@override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
late GoogleMapController _mapController;
Set<Marker> _markers = {};
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
@override
void initState() {
super.initState();
_loadMarkers();
}
// 🔥 CHARGER LES MARQUEURS DEPUIS FIRESTORE
void _loadMarkers() async {
var snapshot = await _firestore.collection('markers').get();
setState(() {
_markers = snapshot.docs.map((doc) {
var data = doc.data();
return Marker(
markerId: MarkerId(doc.id),
position: LatLng(data['lat'], data['lng']),
infoWindow: InfoWindow(title: data['title'] ?? 'Marqueur'),
onTap: () => _showMarkerDialog(doc.id, data['title'] ?? "Sans titre"), // 🟢 Afficher popup
);
}).toSet();
});
}
// 🎯 AJOUTER UN MARQUEUR AU CLIC SUR LA CARTE
void _addMarker(LatLng position) async {
String markerId = DateTime.now().millisecondsSinceEpoch.toString();
Marker newMarker = Marker(
markerId: MarkerId(markerId),
position: position,
infoWindow: InfoWindow(title: 'Nouveau marqueur'),
onTap: () => _showMarkerDialog(markerId, 'Nouveau marqueur'), // 🟢 Afficher popup
);
setState(() {
_markers.add(newMarker);
});
// 🔥 Sauvegarde dans Firestore
await _firestore.collection('markers').doc(markerId).set({
'lat': position.latitude,
'lng': position.longitude,
'title': 'Nouveau marqueur'
});
}
// 📌 AFFICHER BOÎTE D'INFOS + SUPPRESSION
void _showMarkerDialog(String markerId, String title) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text("Que souhaitez-vous faire ?"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Fermer"),
),
TextButton(
onPressed: () {
_deleteMarker(markerId);
Navigator.pop(context);
},
child: Text("Supprimer", style: TextStyle(color: Colors.red)),
),
],
),
);
}
// ❌ SUPPRIMER UN MARQUEUR
void _deleteMarker(String markerId) async {
setState(() {
_markers.removeWhere((marker) => marker.markerId.value == markerId);
});
// 🔥 Supprime de Firestore
await _firestore.collection('markers').doc(markerId).delete();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Carte avec marqueurs")),
body: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(48.8566, 2.3522), // Paris en exemple
zoom: 12,
),
markers: _markers,
onMapCreated: (controller) => _mapController = controller,
onTap: (position) => _addMarker(position), // 🟢 Clic simple sur carte => Ajoute un marqueur
),
);
}
}
Dans cet exemple, l’utilisateur peut ajouter un marqueur sur la carte avec un simple clic. L’endroit est alors enregistré dans Firestore, pour pouvoir s’afficher en temps réel. Cliquer sur un marqueur ouvre une fenêtre contextuelle avec le titre du marqueur et un bouton « supprimer » qui retire le marqueur de Firestore, et donc de la carte.

Afficher des marqueurs sur une carte Google
Une fois votre carte Google ajouté à votre application, il vous est possible de lui ajouter des marqueurs, grâce à la propriété markers
, comme suit :
GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(48.8566, 2.3522), // Position de Paris
zoom: 12, // Niveau de zoom
),
markers: _markers, // Marqueurs à afficher sur la carte
onMapCreated: (controller) => _mapController = controller, // Initialisation du contrôleur de la carte
onTap: (position) => _addMarker(position), // Ajouter un marqueur lors du clic sur la carte
)
Ici, la variable _markers, correspond à la liste des marqueurs à afficher, qui est de type Set<Marker>
. Dans mon exemple, je les récupère dans une collection Firestore, de la manière suivante :
Set<Marker> _markers = {};
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
@override
void initState() {
super.initState();
_loadMarkers();
}
// 🔥 CHARGER LES MARQUEURS DEPUIS FIRESTORE
void _loadMarkers() async {
var snapshot = await _firestore.collection('markers').get();
setState(() {
_markers = snapshot.docs.map((doc) {
var data = doc.data();
return Marker(
markerId: MarkerId(doc.id),
position: LatLng(data['lat'], data['lng']),
infoWindow: InfoWindow(title: data['title'] ?? 'Marqueur'),
onTap: () => _showMarkerDialog(doc.id, data['title'] ?? "Sans titre"), // 🟢 Afficher popup
);
}).toSet();
});
}
Comment la position sur la carte est-elle récupérée ?
Lorsque l’utilisateur clique sur un endroit de la carte, la fonction onTap
du widget GoogleMap
est automatiquement appelée. Cette fonction reçoit en paramètre la position du clic sous forme d’un objet LatLng
, qui est une classe définie par l’API de Google Maps. C’est cet objet LatLng
qui contient les coordonnées géographiques du point cliqué, à savoir la latitude et la longitude.
Voici ce qui se passe exactement :
1. GoogleMap Widget : Dans le widget GoogleMap
, on définit un gestionnaire d’événements pour la fonction onTap
, comme ceci :
onTap: (position) => _addMarker(position),
Cette ligne de code signifie que chaque fois que l’utilisateur clique quelque part sur la carte, la fonction onTap
récupère les coordonnées de l’endroit où l’utilisateur a cliqué et les transmet sous forme d’un objet LatLng
à la fonction _addMarker
.
2. LatLng : L’objet position
est donc une instance de la classe LatLng
, qui contient deux propriétés importantes :
latitude
: La latitude du lieu où l’utilisateur a cliqué.longitude
: La longitude du lieu où l’utilisateur a cliqué.
Par exemple, si l’utilisateur clique sur un endroit particulier de la carte, position.latitude
et position.longitude
contiendront les valeurs exactes de latitude et de longitude correspondant à cet endroit précis.
3. Passage de la position à la fonction _addMarker : Une fois que la position est récupérée sous forme d’objet LatLng
, elle est ensuite transmise à la fonction _addMarker
. Cette fonction utilise les coordonnées latitude
et longitude
pour positionner le marqueur à l’endroit exact où l’utilisateur a cliqué sur la carte.
Créer un marqueur en cliquant sur la carte
Ensuite, on va permettre à un utilisateur d’ajouter un marqueur sur la carte, à l’endroit où il clique et de l’enregistrer dans Firestore :
//Fonction à placer au niveau de la propriété onTap de la carte
void _addMarker(LatLng position) async {
String markerId = DateTime.now().millisecondsSinceEpoch.toString(); // Génération d'un ID unique
Marker newMarker = Marker(
markerId: MarkerId(markerId),
position: position, // Position du marqueur
infoWindow: InfoWindow(title: 'Nouveau marqueur'), // Titre du marqueur
onTap: () => _showMarkerDialog(markerId, 'Nouveau marqueur'), // Action au clic sur le marqueur
);
// Sauvegarde du marqueur dans Firestore pour le rendre persistant
await _firestore.collection('markers').doc(markerId).set({
'lat': position.latitude,
'lng': position.longitude,
'title': 'Nouveau marqueur',
});
setState(() {
_markers.add(newMarker); // Ajout du marqueur à la liste des marqueurs
});
}
Explication:
- _addMarker(LatLng position) : Cette fonction est appelée lorsque l’utilisateur clique sur la carte.
- markerId : Chaque marqueur a un identifiant unique. Ici, nous utilisons
DateTime.now().millisecondsSinceEpoch.toString()
pour générer un ID unique. - Marker : Le marqueur est créé avec sa position, un titre et une action (
onTap
) qui déclenche une boîte de dialogue lorsque l’utilisateur clique dessus. - setState : Cette méthode permet de mettre à jour l’interface utilisateur pour afficher le nouveau marqueur sur la carte.
Afficher les informations du marqueur
Dans cet exemple, je souhaite afficher les informations du marqueur dans une information contextuelle, quand l’utilisateur clique dessus :
//Fonction à placer au niveau de la propriété onTap du marqueur
void _showMarkerDialog(String markerId, String title) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text("Que souhaitez-vous faire ?"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context), // Fermer la boîte de dialogue
child: Text("Fermer"),
),
TextButton(
onPressed: () {
_deleteMarker(markerId); // Supprimer le marqueur
Navigator.pop(context);
},
child: Text("Supprimer", style: TextStyle(color: Colors.red)), // Supprimer le marqueur
),
],
),
);
}
- showDialog : Cette fonction Flutter affiche une boîte de dialogue.
- AlertDialog : Le widget utilisé pour créer une boîte de dialogue avec un titre, un contenu et des actions.
- onPressed : Les actions « Fermer » et « Supprimer » permettent de fermer la boîte ou de supprimer le marqueur.
Supprimer un marqueur
Pour supprimer un marqueur de la carte et de la base de données Firestore, nous utilisons une fonction dédiée. Voici comment cela fonctionne :
void _deleteMarker(String markerId) async {
setState(() {
_markers.removeWhere((marker) => marker.markerId.value == markerId); // Retirer le marqueur de l'affichage
});
// Supprimer le marqueur de Firestore
await _firestore.collection('markers').doc(markerId).delete();
}
La fonction _deleteMarker supprime un marqueur en deux étapes :
- Il est retiré de la liste
_markers
(qui est liée à l’interface utilisateur). - Il est supprimé de Firestore, garantissant que le marqueur est supprimé de la base de données.
Personnaliser les marqueurs
Voici quelques façons de personnaliser vos marqueurs.
Changer la couleur d’un marqueur
Il est possible de modifier la couleur des marqueurs avec des couleurs prédéfinies :
Marker(
markerId: MarkerId("id-1"),
position: LatLng(48.8566, 2.3522),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), // Marqueur bleu
)
Voici les couleurs disponibles :
- BitmapDescriptor.hueRed : Rouge
- BitmapDescriptor.hueGreen : Vert
- BitmapDescriptor.hueBlue : Bleu
- BitmapDescriptor.hueYellow : Jaune
Utiliser une icône personnalisée
Vous pouvez également utiliser une image personnalisée comme icône pour le marqueur :
Marker(
markerId: MarkerId("custom-marker"),
position: LatLng(48.8566, 2.3522),
icon: BitmapDescriptor.fromAssetImage(
ImageConfiguration(size: Size(48, 48)),
'assets/custom_marker.png', // Image personnalisée
),
)
Modifier la taille de l’icône
Enfin, vous pouvez aussi modifier la taille de l’icône personnalisée pour l’adapter à vos besoins :
BitmapDescriptor.fromAssetImage(
ImageConfiguration(devicePixelRatio: 2.5), // Ajuster la taille
'assets/custom_marker.png',
)