Add a Popup in Flutter
This tutorial shows how to display popups (info windows) on markers in your MapMetrics Flutter map. Popups are great for showing extra information when a user taps on a marker.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Basic Popup with InfoWindow
The simplest way to show a popup is by using the InfoWindow property on a Marker:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class PopupExampleScreen extends StatefulWidget {
@override
_PopupExampleScreenState createState() => _PopupExampleScreenState();
}
class _PopupExampleScreenState extends State<PopupExampleScreen> {
MapMetricsController? mapController;
final Set<Marker> markers = {
Marker(
markerId: MarkerId('paris'),
position: LatLng(48.852966, 2.349902),
infoWindow: InfoWindow(
title: 'Hello MapMetrics',
snippet: 'A good coffee shop',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
),
};
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Popup Example')),
body: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(48.852966, 2.349902),
zoom: 13.0,
),
markers: markers,
),
);
}
}Tap on the marker to see the popup with the title and snippet text.
Multiple Markers with Popups
Add several markers, each with their own popup content:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class MultiplePopupsScreen extends StatefulWidget {
@override
_MultiplePopupsScreenState createState() => _MultiplePopupsScreenState();
}
class _MultiplePopupsScreenState extends State<MultiplePopupsScreen> {
MapMetricsController? mapController;
final Set<Marker> markers = {
Marker(
markerId: MarkerId('eiffel_tower'),
position: LatLng(48.8584, 2.2945),
infoWindow: InfoWindow(
title: 'Eiffel Tower',
snippet: 'Iconic iron lattice tower on the Champ de Mars',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue),
),
Marker(
markerId: MarkerId('louvre'),
position: LatLng(48.8606, 2.3376),
infoWindow: InfoWindow(
title: 'Louvre Museum',
snippet: 'World\'s largest art museum and historic monument',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
),
Marker(
markerId: MarkerId('notre_dame'),
position: LatLng(48.8530, 2.3499),
infoWindow: InfoWindow(
title: 'Notre-Dame Cathedral',
snippet: 'Medieval Catholic cathedral on the Île de la Cité',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
),
};
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Paris Landmarks')),
body: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(48.8566, 2.3222),
zoom: 13.0,
),
markers: markers,
),
);
}
}Custom Popup with Bottom Sheet
For richer popup content beyond a simple title and snippet, you can show a bottom sheet when a marker is tapped:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class CustomPopupScreen extends StatefulWidget {
@override
_CustomPopupScreenState createState() => _CustomPopupScreenState();
}
class _CustomPopupScreenState extends State<CustomPopupScreen> {
MapMetricsController? mapController;
final Map<String, Map<String, String>> markerData = {
'cafe': {
'title': 'Le Petit Café',
'description': 'A cozy Parisian café with excellent croissants and espresso.',
'hours': 'Mon-Sat: 7:00 AM - 9:00 PM',
'rating': '4.5',
},
'bookstore': {
'title': 'Shakespeare & Company',
'description': 'Historic English-language bookstore on the Left Bank.',
'hours': 'Daily: 10:00 AM - 10:00 PM',
'rating': '4.8',
},
};
Set<Marker> get markers => {
Marker(
markerId: MarkerId('cafe'),
position: LatLng(48.8530, 2.3470),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
),
Marker(
markerId: MarkerId('bookstore'),
position: LatLng(48.8526, 2.3471),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet),
),
};
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Custom Popups')),
body: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
onMarkerTapped: (MarkerId markerId) {
_showCustomPopup(markerId.value);
},
initialCameraPosition: CameraPosition(
target: LatLng(48.8530, 2.3470),
zoom: 17.0,
),
markers: markers,
),
);
}
void _showCustomPopup(String markerId) {
final data = markerData[markerId];
if (data == null) return;
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return Padding(
padding: EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data['title']!,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
data['description']!,
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
),
SizedBox(height: 12),
Row(
children: [
Icon(Icons.access_time, size: 16, color: Colors.grey),
SizedBox(width: 4),
Text(data['hours']!, style: TextStyle(fontSize: 13)),
],
),
SizedBox(height: 8),
Row(
children: [
Icon(Icons.star, size: 16, color: Colors.amber),
SizedBox(width: 4),
Text(data['rating']!, style: TextStyle(fontSize: 13)),
],
),
SizedBox(height: 16),
],
),
);
},
);
}
}Add Popup on Map Tap
You can also show a popup when the user taps anywhere on the map:
MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (controller) => mapController = controller,
onMapClick: (Point point, LatLng coordinates) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Location'),
content: Text(
'Lat: ${coordinates.latitude.toStringAsFixed(6)}\n'
'Lng: ${coordinates.longitude.toStringAsFixed(6)}',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
},
initialCameraPosition: CameraPosition(
target: LatLng(48.852966, 2.349902),
zoom: 13.0,
),
)Next Steps
- Add a Polyline — Draw lines and routes on the map
- Fly to a Location — Animate the camera to different places
- Draggable Marker — Let users drag markers around the map
Tip: For simple information, use InfoWindow. For richer content with images, buttons, or custom layouts, use a bottom sheet or dialog triggered by onMarkerTapped.