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:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
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):
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:
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::
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:
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 type | Carries | Description |
|---|---|---|
MapEventClick | point (Position) | User taps the map |
MapEventDoubleClick | point (Position) | User double-taps the map |
MapEventLongClick | point (Position) | User long-presses the map |
MapEventSecondaryClick | point (Position) | Secondary click (desktop/web right-click) |
MapEventMoveCamera | camera (MapCamera) | Camera position changes |
MapEventStartMoveCamera | reason | Camera has started moving |
MapEventCameraIdle | — | Camera stops moving |
MapEventIdle | — | Map has finished rendering the current frame |
MapEventMapCreated | — | Native map instance created |
MapEventStyleLoaded | — | Style finished loading (also available as the separate onStyleLoaded callback) |
Next Steps
- Locate the User — Show user's GPS position
- Markers and Annotations — Drag a widget marker to get coordinates
- Map Interactions — Full gesture and event handling
Tip: Use MapEventCameraIdle instead of MapEventMoveCamera if you only need the final position after panning — this avoids excessive rebuilds during fast panning.