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

Add a GeoJSON Polygon in Flutter

This tutorial shows how to add a GeoJSON Polygon to your MapMetrics Flutter map — perfect for highlighting regions, zones, or areas.

Prerequisites

Before you begin, ensure you have:

Basic GeoJSON Polygon

Add a filled polygon from GeoJSON data:

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

class GeoJsonPolygonScreen extends StatefulWidget {
  @override
  _GeoJsonPolygonScreenState createState() => _GeoJsonPolygonScreenState();
}

class _GeoJsonPolygonScreenState extends State<GeoJsonPolygonScreen> {
  MapController? mapController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GeoJSON Polygon')),
      body: MapMetricsView(
        options: MapOptions(
          initCenter: Position(2.35, 48.8), // lng, lat
          initZoom: 11.0,
          initStyle:
              'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
        ),
        onMapCreated: (MapController controller) {
          mapController = controller;
        },
        onStyleLoaded: (StyleController style) {
          _addGeoJsonPolygon(style);
        },
      ),
    );
  }

  Future<void> _addGeoJsonPolygon(StyleController style) async {
    // GeoJSON coordinates are already [lng, lat] — no swapping needed here.
    final geoJson = {
      'type': 'Feature',
      'properties': {'name': 'Central Paris'},
      'geometry': {
        'type': 'Polygon',
        'coordinates': [
          [
            [2.3200, 48.8400],
            [2.3800, 48.8400],
            [2.3800, 48.8700],
            [2.3200, 48.8700],
            [2.3200, 48.8400], // close the ring
          ]
        ],
      },
    };

    // Add the GeoJSON source
    await style.addSource(
      GeoJsonSource(id: 'region-source', data: jsonEncode(geoJson)),
    );

    // Add a fill layer
    await style.addLayer(
      const FillStyleLayer(
        id: 'region-fill',
        sourceId: 'region-source',
        paint: {'fill-color': '#3b82f6', 'fill-opacity': 0.3},
      ),
    );

    // Add an outline layer
    await style.addLayer(
      const LineStyleLayer(
        id: 'region-outline',
        sourceId: 'region-source',
        paint: {'line-color': '#1d4ed8', 'line-width': 2.0},
      ),
    );
  }
}

Multiple Polygons with FeatureCollection

Display several regions with different colors:

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

class MultiPolygonScreen extends StatefulWidget {
  @override
  _MultiPolygonScreenState createState() => _MultiPolygonScreenState();
}

class _MultiPolygonScreenState extends State<MultiPolygonScreen> {
  MapController? mapController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Multiple Polygons')),
      body: MapMetricsView(
        options: MapOptions(
          initCenter: Position(8.0, 48.0), // lng, lat
          initZoom: 4.0,
          initStyle:
              'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
        ),
        onMapCreated: (MapController controller) {
          mapController = controller;
        },
        onStyleLoaded: (StyleController style) {
          _addMultiplePolygons(style);
        },
      ),
    );
  }

  Future<void> _addMultiplePolygons(StyleController style) async {
    final featureCollection = {
      'type': 'FeatureCollection',
      'features': [
        {
          'type': 'Feature',
          'properties': {'name': 'France', 'color': 'blue'},
          'geometry': {
            'type': 'Polygon',
            'coordinates': [
              [
                [-1.75, 43.3],
                [3.0, 43.3],
                [7.7, 43.8],
                [8.2, 48.9],
                [2.5, 51.1],
                [-4.8, 48.5],
                [-1.75, 43.3],
              ]
            ],
          },
        },
        {
          'type': 'Feature',
          'properties': {'name': 'Germany', 'color': 'red'},
          'geometry': {
            'type': 'Polygon',
            'coordinates': [
              [
                [5.9, 47.3],
                [15.0, 47.3],
                [15.0, 55.0],
                [5.9, 55.0],
                [5.9, 47.3],
              ]
            ],
          },
        },
      ],
    };

    await style.addSource(
      GeoJsonSource(id: 'countries', data: jsonEncode(featureCollection)),
    );

    // Fill layer with semi-transparent color
    await style.addLayer(
      const FillStyleLayer(
        id: 'countries-fill',
        sourceId: 'countries',
        paint: {'fill-color': '#3b82f6', 'fill-opacity': 0.2},
      ),
    );

    // Outline layer
    await style.addLayer(
      const LineStyleLayer(
        id: 'countries-outline',
        sourceId: 'countries',
        paint: {'line-color': '#1e40af', 'line-width': 2.0},
      ),
    );
  }
}

Polygon with Hole

Create a polygon with a cutout hole inside:

dart
Future<void> _addPolygonWithHole(StyleController style) async {
  final geoJson = {
    'type': 'Feature',
    'properties': {},
    'geometry': {
      'type': 'Polygon',
      'coordinates': [
        // Outer ring
        [
          [2.2800, 48.8200],
          [2.4200, 48.8200],
          [2.4200, 48.8900],
          [2.2800, 48.8900],
          [2.2800, 48.8200],
        ],
        // Inner ring (hole)
        [
          [2.3300, 48.8450],
          [2.3700, 48.8450],
          [2.3700, 48.8650],
          [2.3300, 48.8650],
          [2.3300, 48.8450],
        ],
      ],
    },
  };

  await style.addSource(
    GeoJsonSource(id: 'polygon-hole', data: jsonEncode(geoJson)),
  );

  await style.addLayer(
    const FillStyleLayer(
      id: 'polygon-hole-fill',
      sourceId: 'polygon-hole',
      paint: {'fill-color': '#22c55e', 'fill-opacity': 0.4},
    ),
  );

  await style.addLayer(
    const LineStyleLayer(
      id: 'polygon-hole-outline',
      sourceId: 'polygon-hole',
      paint: {'line-color': '#15803d', 'line-width': 2.0},
    ),
  );
}

GeoJSON Polygon Properties

FillStyleLayer and LineStyleLayer take plain MapLibre style spec keys in their paint maps:

PropertyLayerTypeDescription
fill-colorFillStyleLayerStringFill color as hex string
fill-opacityFillStyleLayerdoubleFill opacity from 0.0 to 1.0
line-colorLineStyleLayerStringOutline color as hex string
line-widthLineStyleLayerdoubleOutline width in pixels

Next Steps


Tip: Always close polygon rings — the first and last coordinate must be identical. Use FeatureCollection to group multiple polygons into one source for better performance.