Markers and Annotations with Flutter and MapMetrics
This tutorial will show you how to add markers, annotations, and custom overlays to your MapMetrics map in Flutter.
Adding Basic Markers
Here's how to add simple markers to your map:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class MarkersScreen extends StatefulWidget {
@override
_MarkersScreenState createState() => _MarkersScreenState();
}
class _MarkersScreenState extends State<MarkersScreen> {
MapMetricsController? mapController;
Set<Marker> markers = {};
@override
void initState() {
super.initState();
_initializeMarkers();
}
void _initializeMarkers() {
markers = {
Marker(
markerId: MarkerId('new_york'),
position: LatLng(40.7128, -74.0060),
infoWindow: InfoWindow(
title: 'New York City',
snippet: 'The Big Apple',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
),
Marker(
markerId: MarkerId('san_francisco'),
position: LatLng(37.7749, -122.4194),
infoWindow: InfoWindow(
title: 'San Francisco',
snippet: 'The Golden Gate City',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue),
),
Marker(
markerId: MarkerId('chicago'),
position: LatLng(41.8781, -87.6298),
infoWindow: InfoWindow(
title: 'Chicago',
snippet: 'The Windy City',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
),
};
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MapMetrics Markers'),
actions: [
IconButton(
icon: Icon(Icons.add_location),
onPressed: _addRandomMarker,
),
],
),
body: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
setState(() {
mapController = controller;
});
},
onMapClick: (Point point, LatLng coordinates) {
_addMarkerAtLocation(coordinates);
},
initialCameraPosition: CameraPosition(
target: LatLng(39.8283, -98.5795), // Center of USA
zoom: 4.0,
),
markers: markers,
),
floatingActionButton: FloatingActionButton(
onPressed: _clearMarkers,
child: Icon(Icons.clear_all),
),
);
}
void _addMarkerAtLocation(LatLng position) {
final String markerId = 'marker_${DateTime.now().millisecondsSinceEpoch}';
final Marker marker = Marker(
markerId: MarkerId(markerId),
position: position,
infoWindow: InfoWindow(
title: 'Custom Marker',
snippet: 'Added at ${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}',
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
);
setState(() {
markers.add(marker);
});
}
void _addRandomMarker() {
final double lat = 25.0 + (Random().nextDouble() * 20.0); // 25-45 latitude
final double lng = -125.0 + (Random().nextDouble() * 50.0); // -125 to -75 longitude
_addMarkerAtLocation(LatLng(lat, lng));
}
void _clearMarkers() {
setState(() {
markers.clear();
});
}
}
## Custom Marker Icons
### Using Custom Images
```dart
Marker(
markerId: MarkerId('custom_icon'),
position: LatLng(40.7128, -74.0060),
icon: BitmapDescriptor.fromAssetImage(
ImageConfiguration(size: Size(48, 48)),
'assets/images/custom_marker.png',
),
infoWindow: InfoWindow(title: 'Custom Icon'),
)
Using Different Default Colors
dart
// Available default colors
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueYellow)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRose)
BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueAzure)
Using Network Images
dart
Marker(
markerId: MarkerId('network_icon'),
position: LatLng(40.7128, -74.0060),
icon: BitmapDescriptor.fromNetworkImage(
ImageConfiguration(size: Size(48, 48)),
'https://example.com/marker-icon.png',
),
)
Marker Interactions
Handle Marker Taps
dart
MapMetrics(
// ... other properties
onMarkerTapped: (MarkerId markerId) {
print('Marker tapped: ${markerId.value}');
_showMarkerInfo(markerId);
},
markers: markers,
)
void _showMarkerInfo(MarkerId markerId) {
final Marker? marker = markers.firstWhere(
(m) => m.markerId == markerId,
orElse: () => null,
);
if (marker != null) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(marker.infoWindow.title ?? 'Marker'),
content: Text(marker.infoWindow.snippet ?? ''),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Close'),
),
],
),
);
}
}
Clustered Markers
For better performance with many markers, implement clustering:
dart
class ClusteredMarkersScreen extends StatefulWidget {
@override
_ClusteredMarkersScreenState createState() => _ClusteredMarkersScreenState();
}
class _ClusteredMarkersScreenState extends State<ClusteredMarkersScreen> {
MapMetricsController? mapController;
Set<Marker> markers = {};
List<LatLng> clusterPoints = [];
@override
void initState() {
super.initState();
_generateClusterPoints();
}
void _generateClusterPoints() {
// Generate random points around a center
final LatLng center = LatLng(40.7128, -74.0060);
clusterPoints = List.generate(50, (index) {
final double latOffset = (Random().nextDouble() - 0.5) * 0.1;
final double lngOffset = (Random().nextDouble() - 0.5) * 0.1;
return LatLng(
center.latitude + latOffset,
center.longitude + lngOffset,
);
});
_updateMarkers();
}
void _updateMarkers() {
markers = clusterPoints.asMap().entries.map((entry) {
final int index = entry.key;
final LatLng position = entry.value;
return Marker(
markerId: MarkerId('cluster_$index'),
position: position,
infoWindow: InfoWindow(
title: 'Point $index',
snippet: 'Generated point',
),
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueRed,
),
);
}).toSet();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Clustered Markers')),
body: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (controller) => mapController = controller,
initialCameraPosition: CameraPosition(
target: LatLng(40.7128, -74.0060),
zoom: 12.0,
),
markers: markers,
),
);
}
}
Polygons and Polylines
Adding Polygons
dart
Set<Polygon> polygons = {
Polygon(
polygonId: PolygonId('custom_polygon'),
points: [
LatLng(40.7, -74.0),
LatLng(40.7, -73.9),
LatLng(40.8, -73.9),
LatLng(40.8, -74.0),
],
strokeWidth: 2,
strokeColor: Colors.red,
fillColor: Colors.red.withOpacity(0.3),
),
};
MapMetrics(
// ... other properties
polygons: polygons,
)
Adding Polylines
dart
Set<Polyline> polylines = {
Polyline(
polylineId: PolylineId('route'),
points: [
LatLng(40.7128, -74.0060), // New York
LatLng(39.9526, -75.1652), // Philadelphia
LatLng(38.9072, -77.0369), // Washington DC
],
color: Colors.blue,
width: 3,
),
};
MapMetrics(
// ... other properties
polylines: polylines,
)
Circles
dart
Set<Circle> circles = {
Circle(
circleId: CircleId('radius_circle'),
center: LatLng(40.7128, -74.0060),
radius: 5000, // meters
strokeWidth: 2,
strokeColor: Colors.blue,
fillColor: Colors.blue.withOpacity(0.2),
),
};
MapMetrics(
// ... other properties
circles: circles,
)
Advanced Marker Features
Draggable Markers
dart
Marker(
markerId: MarkerId('draggable'),
position: LatLng(40.7128, -74.0060),
draggable: true,
onDragEnd: (LatLng newPosition) {
print('Marker moved to: $newPosition');
},
)
Flat Markers (No Perspective)
dart
Marker(
markerId: MarkerId('flat_marker'),
position: LatLng(40.7128, -74.0060),
flat: true, // Marker stays flat regardless of map tilt
)
Marker Rotation
dart
Marker(
markerId: MarkerId('rotated_marker'),
position: LatLng(40.7128, -74.0060),
rotation: 45.0, // Rotate marker 45 degrees
)
Performance Optimization
Marker Management
dart
class OptimizedMarkersScreen extends StatefulWidget {
@override
_OptimizedMarkersScreenState createState() => _OptimizedMarkersScreenState();
}
class _OptimizedMarkersScreenState extends State<OptimizedMarkersScreen> {
MapMetricsController? mapController;
Set<Marker> visibleMarkers = {};
List<LatLng> allPoints = [];
@override
void initState() {
super.initState();
_loadAllPoints();
}
void _loadAllPoints() {
// Load all your data points here
allPoints = [/* your data */];
}
void _updateVisibleMarkers(CameraPosition position) {
// Only show markers within the current viewport
final double zoom = position.zoom;
final LatLng center = position.target;
// Calculate visible bounds based on zoom level
final double latDelta = 180.0 / pow(2, zoom);
final double lngDelta = 360.0 / pow(2, zoom);
final visiblePoints = allPoints.where((point) {
return (point.latitude - center.latitude).abs() < latDelta &&
(point.longitude - center.longitude).abs() < lngDelta;
}).take(100); // Limit to 100 markers for performance
setState(() {
visibleMarkers = visiblePoints.map((point) => Marker(
markerId: MarkerId('point_${point.latitude}_${point.longitude}'),
position: point,
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
)).toSet();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Optimized Markers')),
body: MapMetrics(
styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (controller) => mapController = controller,
onCameraMove: _updateVisibleMarkers,
initialCameraPosition: CameraPosition(
target: LatLng(40.7128, -74.0060),
zoom: 10.0,
),
markers: visibleMarkers,
),
);
}
}
Next Steps
Now that you can add markers and annotations, try:
Pro Tip: Use the MapMetrics Portal to create custom map styles that complement your markers. You can adjust the map's color scheme to make your markers stand out better.