Skip to content

Add a Polygon in Flutter

This tutorial shows how to draw filled polygons on your MapMetrics Flutter map. Polygons are useful for highlighting areas, zones, or boundaries.

Prerequisites

Before you begin, ensure you have:

Basic Polygon

Draw a simple polygon by providing a list of LatLng points. The shape will automatically close by connecting the last point to the first:

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

class PolygonExampleScreen extends StatefulWidget {
  @override
  _PolygonExampleScreenState createState() => _PolygonExampleScreenState();
}

class _PolygonExampleScreenState extends State<PolygonExampleScreen> {
  MapMetricsController? mapController;

  final Set<Polygon> polygons = {
    Polygon(
      polygonId: PolygonId('manhattan'),
      points: [
        LatLng(40.800, -73.958),
        LatLng(40.800, -74.020),
        LatLng(40.700, -74.020),
        LatLng(40.700, -73.970),
      ],
      strokeWidth: 2,
      strokeColor: Colors.blue,
      fillColor: Colors.blue.withOpacity(0.2),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Polygon Example')),
      body: MapMetrics(
        styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(40.750, -73.990),
          zoom: 12.0,
        ),
        polygons: polygons,
      ),
    );
  }
}

Multiple Colored Zones

Show different areas with distinct colors:

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

class ColoredZonesScreen extends StatefulWidget {
  @override
  _ColoredZonesScreenState createState() => _ColoredZonesScreenState();
}

class _ColoredZonesScreenState extends State<ColoredZonesScreen> {
  MapMetricsController? mapController;

  final Set<Polygon> zones = {
    // Zone A - Green (safe zone)
    Polygon(
      polygonId: PolygonId('zone_a'),
      points: [
        LatLng(48.870, 2.330),
        LatLng(48.870, 2.350),
        LatLng(48.860, 2.350),
        LatLng(48.860, 2.330),
      ],
      strokeWidth: 2,
      strokeColor: Colors.green,
      fillColor: Colors.green.withOpacity(0.25),
    ),
    // Zone B - Orange (caution zone)
    Polygon(
      polygonId: PolygonId('zone_b'),
      points: [
        LatLng(48.860, 2.330),
        LatLng(48.860, 2.350),
        LatLng(48.850, 2.350),
        LatLng(48.850, 2.330),
      ],
      strokeWidth: 2,
      strokeColor: Colors.orange,
      fillColor: Colors.orange.withOpacity(0.25),
    ),
    // Zone C - Red (restricted zone)
    Polygon(
      polygonId: PolygonId('zone_c'),
      points: [
        LatLng(48.850, 2.330),
        LatLng(48.850, 2.350),
        LatLng(48.840, 2.350),
        LatLng(48.840, 2.330),
      ],
      strokeWidth: 2,
      strokeColor: Colors.red,
      fillColor: Colors.red.withOpacity(0.25),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Colored Zones')),
      body: Stack(
        children: [
          MapMetrics(
            styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
            onMapCreated: (controller) => mapController = controller,
            initialCameraPosition: CameraPosition(
              target: LatLng(48.855, 2.340),
              zoom: 14.0,
            ),
            polygons: zones,
          ),
          // Legend
          Positioned(
            top: 16,
            right: 16,
            child: Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.white.withOpacity(0.9),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  _legendItem(Colors.green, 'Safe Zone'),
                  SizedBox(height: 6),
                  _legendItem(Colors.orange, 'Caution Zone'),
                  SizedBox(height: 6),
                  _legendItem(Colors.red, 'Restricted Zone'),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _legendItem(Color color, String label) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          width: 16,
          height: 16,
          decoration: BoxDecoration(
            color: color.withOpacity(0.3),
            border: Border.all(color: color, width: 2),
            borderRadius: BorderRadius.circular(3),
          ),
        ),
        SizedBox(width: 8),
        Text(label, style: TextStyle(fontSize: 13)),
      ],
    );
  }
}

Tappable Polygons

Make polygons respond to taps:

dart
Polygon(
  polygonId: PolygonId('tappable_area'),
  points: [
    LatLng(48.870, 2.330),
    LatLng(48.870, 2.360),
    LatLng(48.850, 2.360),
    LatLng(48.850, 2.330),
  ],
  strokeWidth: 2,
  strokeColor: Colors.purple,
  fillColor: Colors.purple.withOpacity(0.2),
  consumeTapEvents: true,
  onTap: () {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Area Selected'),
        content: Text('You tapped on the highlighted area.'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('OK'),
          ),
        ],
      ),
    );
  },
)

Polygon Properties

PropertyTypeDescription
polygonIdPolygonIdUnique identifier for the polygon
pointsList<LatLng>Vertices of the polygon (auto-closed)
strokeWidthintBorder width in pixels
strokeColorColorBorder color
fillColorColorFill color (use withOpacity for transparency)
visibleboolWhether the polygon is visible
zIndexintDrawing order relative to other overlays
consumeTapEventsboolIf true, tap events are consumed by the polygon
onTapVoidCallbackCalled when the polygon is tapped

Next Steps


Tip: Use withOpacity on your fill colors to keep the polygon semi-transparent so the map underneath remains visible.