Skip to content

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.