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:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
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:
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:
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:
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:
| Property | Map | Type | Description |
|---|---|---|---|
line-color | paint | String | Line color as hex string |
line-width | paint | double | Width of the line in pixels |
line-opacity | paint | double | Opacity from 0.0 to 1.0 |
line-dasharray | paint | List<double> | Dash and gap lengths |
line-join | layout | String | How line segments join: round, bevel, miter |
line-cap | layout | String | Shape at line ends: round, butt, square |
Next Steps
- Add a GeoJSON Polygon — Draw filled areas from GeoJSON
- Draw GeoJSON Points — Render point data on the map
- Animate a Line — Animate a line being drawn
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.