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 Line in Flutter

This tutorial shows how to add a GeoJSON LineString to your MapMetrics Flutter map using a source and layer approach — ideal for routes, borders, or paths.

Prerequisites

Before you begin, ensure you have:

Basic GeoJSON Line

Add a GeoJSON line source and render it as a styled line layer. GeoJsonSource.data takes a GeoJSON string (or a URL to one), so encode the map with jsonEncode before passing it in:

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

class GeoJsonLineScreen extends StatefulWidget {
  @override
  _GeoJsonLineScreenState createState() => _GeoJsonLineScreenState();
}

class _GeoJsonLineScreenState extends State<GeoJsonLineScreen> {
  MapController? mapController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GeoJSON Line')),
      body: MapMetricsView(
        options: MapOptions(
          initCenter: Position(10.0, 50.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) {
          _addGeoJsonLine(style);
        },
      ),
    );
  }

  Future<void> _addGeoJsonLine(StyleController style) async {
    // Define the GeoJSON data. GeoJSON coordinates are already [lng, lat] —
    // no swapping needed here, unlike Position(lng, lat) constructor calls.
    final geoJson = {
      'type': 'Feature',
      'properties': {},
      'geometry': {
        'type': 'LineString',
        'coordinates': [
          [2.349902, 48.852966], // Paris
          [-0.1276, 51.5074], // London
          [13.405, 52.52], // Berlin
          [16.3738, 48.2082], // Vienna
          [12.4964, 41.9028], // Rome
        ],
      },
    };

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

    // Add a line layer using the source
    await style.addLayer(
      const LineStyleLayer(
        id: 'route-layer',
        sourceId: 'route-source',
        layout: {'line-join': 'round', 'line-cap': 'round'},
        paint: {'line-color': '#3b82f6', 'line-width': 4.0},
      ),
    );
  }
}

Styled GeoJSON Line

Customize the line with dashes, opacity, and width:

dart
Future<void> _addStyledLine(StyleController style) async {
  final geoJson = {
    'type': 'Feature',
    'properties': {},
    'geometry': {
      'type': 'LineString',
      'coordinates': [
        [-3.7038, 40.4168], // Madrid
        [2.349902, 48.853], // Paris
        [13.405, 52.52], // Berlin
      ],
    },
  };

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

  await style.addLayer(
    const LineStyleLayer(
      id: 'styled-route-layer',
      sourceId: 'styled-route',
      layout: {'line-join': 'round', 'line-cap': 'round'},
      paint: {
        'line-color': '#ef4444',
        'line-width': 5.0,
        'line-opacity': 0.8,
        'line-dasharray': [2.0, 1.0], // dashed pattern
      },
    ),
  );
}

Multiple GeoJSON Lines

Display several routes with different styles:

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

class MultipleGeoJsonLinesScreen extends StatefulWidget {
  @override
  _MultipleGeoJsonLinesScreenState createState() =>
      _MultipleGeoJsonLinesScreenState();
}

class _MultipleGeoJsonLinesScreenState
    extends State<MultipleGeoJsonLinesScreen> {
  MapController? mapController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Multiple GeoJSON Lines')),
      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) {
          _addMultipleLines(style);
        },
      ),
    );
  }

  Future<void> _addMultipleLines(StyleController style) async {
    // Route 1: Northern Europe
    final northRoute = {
      'type': 'Feature',
      'properties': {},
      'geometry': {
        'type': 'LineString',
        'coordinates': [
          [-0.1276, 51.5074], // London
          [4.9041, 52.3676], // Amsterdam
          [13.405, 52.52], // Berlin
          [21.0122, 52.2297], // Warsaw
        ],
      },
    };

    // Route 2: Southern Europe
    final southRoute = {
      'type': 'Feature',
      'properties': {},
      'geometry': {
        'type': 'LineString',
        'coordinates': [
          [-9.1393, 38.7223], // Lisbon
          [-3.7038, 40.4168], // Madrid
          [2.1734, 41.3851], // Barcelona
          [12.4964, 41.9028], // Rome
          [23.7275, 37.9838], // Athens
        ],
      },
    };

    await style.addSource(
      GeoJsonSource(id: 'north-route', data: jsonEncode(northRoute)),
    );
    await style.addLayer(
      const LineStyleLayer(
        id: 'north-route-layer',
        sourceId: 'north-route',
        layout: {'line-join': 'round', 'line-cap': 'round'},
        paint: {'line-color': '#3b82f6', 'line-width': 4.0},
      ),
    );

    await style.addSource(
      GeoJsonSource(id: 'south-route', data: jsonEncode(southRoute)),
    );
    await style.addLayer(
      const LineStyleLayer(
        id: 'south-route-layer',
        sourceId: 'south-route',
        layout: {'line-join': 'round', 'line-cap': 'round'},
        paint: {
          'line-color': '#ef4444',
          'line-width': 4.0,
          'line-dasharray': [3.0, 2.0],
        },
      ),
    );
  }
}

GeoJSON Line Properties

LineStyleLayer takes plain MapLibre style spec keys in its layout and paint maps:

PropertyMapTypeDescription
line-colorpaintStringLine color as hex string
line-widthpaintdoubleWidth of the line in pixels
line-opacitypaintdoubleOpacity from 0.0 to 1.0
line-dasharraypaintList<double>Dash and gap lengths
line-joinlayoutStringHow line segments join: round, bevel, miter
line-caplayoutStringShape at line ends: round, butt, square

Next Steps


Tip: GeoJSON sources are powerful for displaying dynamic data. You can update the source data at runtime using styleController.updateGeoJsonSource(id: 'source-id', data: jsonEncode(newData)) to reflect real-time changes.