Skip to content

Both copy text to your clipboard — Build with AI copies a setup prompt to paste into Claude Code, Cursor, Codex or Copilot; Copy page as Markdown copies this page to paste into a chat. How it works

Display a Popup on Long Press in Flutter

This tutorial shows how to show a popup when the user long-presses on the map — great for adding new markers, getting location info, or context menus.

Prerequisites

Before you begin, ensure you have:

How long-press detection works on this SDK

There is no onMapLongClick widget callback. Long presses on the map background arrive through onEvent as a MapEventLongClick, carrying the pressed Position at event.point. Long presses on a specific marker (for a per-marker context menu) are caught with a GestureDetector(onLongPressStart: ...) wrapped around that marker's child widget inside a WidgetLayer — the same pattern the SDK's own interactive widget-layer example uses.

Basic Long Press Popup

Show a card 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> {
  MapController? mapController;
  Position? longPressPosition;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Long Press Popup')),
      body: Stack(
        children: [
          MapMetricsView(
            options: MapOptions(
              initStyle:
                  'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
              initCenter: Position(2.3522, 48.8566), // Paris (lng, lat)
              initZoom: 13.0,
            ),
            onMapCreated: (MapController controller) {
              mapController = controller;
            },
            onEvent: (event) {
              if (event is MapEventLongClick) {
                setState(() {
                  longPressPosition = event.point;
                });
              } else if (event is MapEventClick) {
                // Dismiss popup on regular tap
                setState(() {
                  longPressPosition = null;
                });
              }
            },
            mapChildren: [
              if (longPressPosition != null)
                WidgetLayer(
                  markers: [
                    Marker(
                      point: longPressPosition!,
                      size: const Size(32, 32),
                      alignment: Alignment.bottomCenter,
                      child: const Icon(Icons.location_on, color: Colors.purple, size: 32),
                    ),
                  ],
                ),
            ],
          ),
          // 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!.lat.toStringAsFixed(6)}',
                        style: TextStyle(fontFamily: 'monospace'),
                      ),
                      Text(
                        'Lng: ${longPressPosition!.lng.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> {
  MapController? mapController;
  List<Position> userPositions = [];
  int markerCount = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Context Menu'),
        actions: [
          if (userPositions.isNotEmpty)
            TextButton(
              onPressed: () {
                setState(() {
                  userPositions.clear();
                  markerCount = 0;
                });
              },
              child: Text('Clear All',
                  style: TextStyle(color: Colors.white)),
            ),
        ],
      ),
      body: MapMetricsView(
        options: MapOptions(
          initStyle:
              'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
          initCenter: Position(2.3522, 48.8566), // lng, lat
          initZoom: 13.0,
        ),
        onMapCreated: (MapController controller) {
          mapController = controller;
        },
        onEvent: (event) {
          if (event is MapEventLongClick) {
            _showContextMenu(event.point);
          }
        },
        mapChildren: [
          WidgetLayer(
            markers: userPositions.map((position) {
              return Marker(
                point: position,
                size: const Size(28, 28),
                alignment: Alignment.bottomCenter,
                child: const Icon(Icons.location_on, color: Colors.blue, size: 28),
              );
            }).toList(),
          ),
        ],
      ),
    );
  }

  void _showContextMenu(Position 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.lat.toStringAsFixed(4)}, '
                  '${position.lng.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.lat.toStringAsFixed(4)}, ${position.lng.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(Position position) {
    markerCount++;
    setState(() {
      userPositions.add(position);
    });
  }
}

Long Press vs Tap Comparison

GestureHow you detect itBest For
Tap on map backgroundonEventMapEventClickSelect, dismiss, quick actions
Long press on map backgroundonEventMapEventLongClickContext menus, add markers, advanced actions
Tap on a specific markerGestureDetector(onTap:) wrapping the Marker.child in a WidgetLayerShow details for a specific marker
Long press on a specific markerGestureDetector(onLongPressStart:) wrapping the Marker.childPer-marker context menu (edit/delete/move)

Next Steps


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.