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

Get Coordinates on Tap in Flutter

This tutorial shows how to get and display the longitude and latitude coordinates when the user taps on the map — the Flutter equivalent of getting mouse coordinates on web.

Prerequisites

Before you begin, ensure you have:

How Taps Reach Your Code

MapMetricsView has no onMapClick callback. All map gestures — including taps — arrive through the single onEvent: (MapEvent event) callback, using Dart pattern matching on the event's runtime type. A tap produces a MapEventClick, which carries a point of type Position (longitude, latitude — GeoJSON order, not lat, lng):

dart
onEvent: (event) {
  if (event case MapEventClick()) {
    final tapped = event.point; // Position(lng, lat)
  }
}

Basic Tap Coordinates

Display coordinates in a bar at the bottom of the screen:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class TapCoordinatesScreen extends StatefulWidget {
  @override
  _TapCoordinatesScreenState createState() => _TapCoordinatesScreenState();
}

class _TapCoordinatesScreenState extends State<TapCoordinatesScreen> {
  MapController? mapController;
  Position? tappedPosition;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Tap for Coordinates')),
      body: Column(
        children: [
          Expanded(
            child: MapMetricsView(
              options: MapOptions(
                initCenter: Position(2.3522, 48.8566), // lng, lat — Paris
                initZoom: 5,
                initStyle:
                    'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
              ),
              onMapCreated: (MapController controller) {
                mapController = controller;
              },
              onEvent: (event) {
                if (event case MapEventClick()) {
                  setState(() {
                    tappedPosition = event.point;
                  });
                }
              },
            ),
          ),
          // Coordinates bar
          Container(
            padding: EdgeInsets.all(16),
            color: Colors.grey[900],
            width: double.infinity,
            child: Text(
              tappedPosition != null
                  ? 'Lat: ${tappedPosition!.lat.toStringAsFixed(6)}  '
                    'Lng: ${tappedPosition!.lng.toStringAsFixed(6)}'
                  : 'Tap the map to get coordinates',
              style: TextStyle(
                color: Colors.white,
                fontFamily: 'monospace',
                fontSize: 14,
              ),
              textAlign: TextAlign.center,
            ),
          ),
        ],
      ),
    );
  }
}

Tap Coordinates with Marker

Place a marker at the tapped location and show coordinates. Since the real API has no InfoWindow, the "tap to show info" card becomes a Positioned Flutter widget you manage yourself, and the marker itself is drawn with WidgetLayer in mapChildren::

dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mapmetrics/mapmetrics.dart';

class TapMarkerCoordinatesScreen extends StatefulWidget {
  @override
  _TapMarkerCoordinatesScreenState createState() =>
      _TapMarkerCoordinatesScreenState();
}

class _TapMarkerCoordinatesScreenState
    extends State<TapMarkerCoordinatesScreen> {
  MapController? mapController;
  Position? tappedPosition;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Tap Marker Coordinates')),
      body: Stack(
        children: [
          MapMetricsView(
            options: MapOptions(
              initCenter: Position(2.3522, 48.8566),
              initZoom: 10,
              initStyle:
                  'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
            ),
            onMapCreated: (MapController controller) {
              mapController = controller;
            },
            onEvent: (event) {
              if (event case MapEventClick()) {
                setState(() {
                  tappedPosition = event.point;
                });
              }
            },
            mapChildren: [
              if (tappedPosition != null)
                WidgetLayer(
                  markers: [
                    Marker(
                      point: tappedPosition!,
                      size: const Size(40, 40),
                      alignment: Alignment.bottomCenter,
                      child: const Icon(Icons.location_on,
                          color: Colors.blue, size: 40),
                    ),
                  ],
                ),
            ],
          ),
          // Floating coordinate card
          if (tappedPosition != null)
            Positioned(
              top: 16,
              left: 16,
              right: 16,
              child: Card(
                elevation: 4,
                child: Padding(
                  padding: EdgeInsets.all(12),
                  child: Row(
                    children: [
                      Icon(Icons.location_on, color: Colors.blue),
                      SizedBox(width: 8),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Text(
                              'Latitude: ${tappedPosition!.lat.toStringAsFixed(6)}',
                              style: TextStyle(fontFamily: 'monospace'),
                            ),
                            Text(
                              'Longitude: ${tappedPosition!.lng.toStringAsFixed(6)}',
                              style: TextStyle(fontFamily: 'monospace'),
                            ),
                          ],
                        ),
                      ),
                      IconButton(
                        icon: Icon(Icons.copy, size: 20),
                        tooltip: 'Copy coordinates',
                        onPressed: () {
                          final text =
                              '${tappedPosition!.lat.toStringAsFixed(6)}, '
                              '${tappedPosition!.lng.toStringAsFixed(6)}';
                          Clipboard.setData(ClipboardData(text: text));
                          ScaffoldMessenger.of(context).showSnackBar(
                            SnackBar(content: Text('Coordinates copied!')),
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

Track Camera Center Coordinates

Show the coordinates of the map center as the user pans. Camera movement is also delivered through onEvent, as a MapEventMoveCamera carrying a MapCamera with center, zoom, bearing, and pitch:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class CameraCenterScreen extends StatefulWidget {
  @override
  _CameraCenterScreenState createState() => _CameraCenterScreenState();
}

class _CameraCenterScreenState extends State<CameraCenterScreen> {
  MapController? mapController;
  double centerLat = 48.8566;
  double centerLng = 2.3522;
  double currentZoom = 10.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Camera Center Tracker')),
      body: Stack(
        children: [
          MapMetricsView(
            options: MapOptions(
              initCenter: Position(2.3522, 48.8566),
              initZoom: 10,
              initStyle:
                  'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
            ),
            onMapCreated: (MapController controller) {
              mapController = controller;
            },
            onEvent: (event) {
              if (event case MapEventMoveCamera()) {
                setState(() {
                  centerLat = event.camera.center.lat.toDouble();
                  centerLng = event.camera.center.lng.toDouble();
                  currentZoom = event.camera.zoom;
                });
              }
            },
          ),
          // Crosshair at center
          Center(
            child: Icon(Icons.add, color: Colors.red, size: 24),
          ),
          // Info overlay
          Positioned(
            bottom: 16,
            left: 16,
            child: Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.black87,
                borderRadius: BorderRadius.circular(8),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    'Center:',
                    style: TextStyle(color: Colors.white70, fontSize: 11),
                  ),
                  Text(
                    'Lat: ${centerLat.toStringAsFixed(6)}',
                    style: TextStyle(
                      color: Colors.white,
                      fontFamily: 'monospace',
                      fontSize: 13,
                    ),
                  ),
                  Text(
                    'Lng: ${centerLng.toStringAsFixed(6)}',
                    style: TextStyle(
                      color: Colors.white,
                      fontFamily: 'monospace',
                      fontSize: 13,
                    ),
                  ),
                  Text(
                    'Zoom: ${currentZoom.toStringAsFixed(2)}',
                    style: TextStyle(
                      color: Colors.greenAccent,
                      fontFamily: 'monospace',
                      fontSize: 13,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Map Events Reference

All of these arrive through the single onEvent: (MapEvent event) callback — match on the event's type with a Dart pattern (if (event case MapEventClick()) { ... }) rather than binding separate named callbacks:

Event typeCarriesDescription
MapEventClickpoint (Position)User taps the map
MapEventDoubleClickpoint (Position)User double-taps the map
MapEventLongClickpoint (Position)User long-presses the map
MapEventSecondaryClickpoint (Position)Secondary click (desktop/web right-click)
MapEventMoveCameracamera (MapCamera)Camera position changes
MapEventStartMoveCamerareasonCamera has started moving
MapEventCameraIdleCamera stops moving
MapEventIdleMap has finished rendering the current frame
MapEventMapCreatedNative map instance created
MapEventStyleLoadedStyle finished loading (also available as the separate onStyleLoaded callback)

Next Steps


Tip: Use MapEventCameraIdle instead of MapEventMoveCamera if you only need the final position after panning — this avoids excessive rebuilds during fast panning.