Display a Popup on Long Press in Flutter
This tutorial shows how to show a popup when the user long-presses on the map — the Flutter equivalent of hover popups on web. Great for adding new markers, getting location info, or context menus.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Basic Long Press Popup
Show a dialog with coordinates when the user long-presses:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class LongPressPopupScreen extends StatefulWidget {
@override
_LongPressPopupScreenState createState() => _LongPressPopupScreenState();
}
class _LongPressPopupScreenState extends State<LongPressPopupScreen> {
MapMetricsController? mapController;
LatLng? longPressPosition;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Long Press Popup')),
body: Stack(
children: [
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.3522),
zoom: 13.0,
),
onMapLongClick: (Point point, LatLng coordinates) {
setState(() {
longPressPosition = coordinates;
});
},
onMapClick: (Point point, LatLng coordinates) {
// Dismiss popup on regular tap
setState(() {
longPressPosition = null;
});
},
markers: longPressPosition != null
? {
Marker(
markerId: MarkerId('long_press'),
position: longPressPosition!,
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueViolet),
),
}
: {},
),
// Popup card
if (longPressPosition != null)
Positioned(
bottom: 24,
left: 16,
right: 16,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.location_on, color: Colors.purple),
SizedBox(width: 8),
Text('Long Press Location',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold)),
Spacer(),
IconButton(
icon: Icon(Icons.close, size: 20),
onPressed: () {
setState(() {
longPressPosition = null;
});
},
),
],
),
SizedBox(height: 8),
Text(
'Lat: ${longPressPosition!.latitude.toStringAsFixed(6)}',
style: TextStyle(fontFamily: 'monospace'),
),
Text(
'Lng: ${longPressPosition!.longitude.toStringAsFixed(6)}',
style: TextStyle(fontFamily: 'monospace'),
),
],
),
),
),
),
],
),
);
}
}Long Press Context Menu
Show an action menu with options like "Add Marker", "Get Directions", etc.:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class ContextMenuScreen extends StatefulWidget {
@override
_ContextMenuScreenState createState() => _ContextMenuScreenState();
}
class _ContextMenuScreenState extends State<ContextMenuScreen> {
MapMetricsController? mapController;
Set<Marker> userMarkers = {};
int markerCount = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Context Menu'),
actions: [
if (userMarkers.isNotEmpty)
TextButton(
onPressed: () {
setState(() {
userMarkers.clear();
markerCount = 0;
});
},
child: Text('Clear All',
style: TextStyle(color: Colors.white)),
),
],
),
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.3522),
zoom: 13.0,
),
onMapLongClick: (Point point, LatLng coordinates) {
_showContextMenu(coordinates);
},
markers: userMarkers,
),
);
}
void _showContextMenu(LatLng position) {
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Drag handle
Container(
width: 40,
height: 4,
margin: EdgeInsets.only(top: 12),
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(2),
),
),
Padding(
padding: EdgeInsets.all(16),
child: Text(
'${position.latitude.toStringAsFixed(4)}, '
'${position.longitude.toStringAsFixed(4)}',
style: TextStyle(
color: Colors.grey[600], fontFamily: 'monospace'),
),
),
ListTile(
leading: Icon(Icons.add_location, color: Colors.blue),
title: Text('Add Marker'),
onTap: () {
Navigator.pop(context);
_addMarker(position);
},
),
ListTile(
leading: Icon(Icons.directions, color: Colors.green),
title: Text('Get Directions Here'),
onTap: () {
Navigator.pop(context);
// Handle directions
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Directions to ${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}')),
);
},
),
ListTile(
leading: Icon(Icons.copy, color: Colors.orange),
title: Text('Copy Coordinates'),
onTap: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Coordinates copied!')),
);
},
),
ListTile(
leading: Icon(Icons.info_outline, color: Colors.purple),
title: Text('What\'s Here?'),
onTap: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Searching for nearby places...')),
);
},
),
SizedBox(height: 8),
],
),
);
},
);
}
void _addMarker(LatLng position) {
markerCount++;
setState(() {
userMarkers.add(
Marker(
markerId: MarkerId('user_$markerCount'),
position: position,
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueBlue),
infoWindow: InfoWindow(
title: 'Marker $markerCount',
snippet:
'${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}',
),
),
);
});
}
}Long Press vs Tap Comparison
| Gesture | Callback | Best For |
|---|---|---|
| Tap | onMapClick | Select, dismiss, quick actions |
| Long press | onMapLongClick | Context menus, add markers, advanced actions |
| Marker tap | Marker.onTap | Show details for a specific marker |
Next Steps
- Display a Popup — More popup patterns
- Popup on Click — Tap-based popups
- Get Coordinates on Tap — Show tap coordinates
Tip: Long press is the mobile convention for "right-click" context menus. Use showModalBottomSheet for a native-feeling action menu — it's easier to reach with one hand than a popup card at the top of the screen.