Skip to content

Annotation Layers

Annotation layers show your own points, lines and areas on the map. Put them in the MapLibreMap.layers list and change them like any other widget parameter, with setState() or the state management of your choice. The map adds, updates and removes only what changed.

Layer Geometry Built on
CircleLayer Point CircleStyleLayer
MarkerLayer Point SymbolStyleLayer
PolygonLayer Polygon FillStyleLayer
PolylineLayer LineString LineStyleLayer

Flutter widgets on the map go into a WidgetLayer. For large or frequently changing data, add a source and a style layer to the style instead.

Circles

Circles

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@override
Widget build(BuildContext context) {
  return MapLibreMap(
    options: MapOptions(initCamera: .position(center: Geographic(lon: 9.17, lat: 47.68), zoom: 7)),
    layers: [
      CircleLayer(
        points: const [
          Feature(geometry: Point(Geographic(lon: 9.17, lat: 47.68))),
          Feature(geometry: Point(Geographic(lon: 9.17, lat: 48))),
          Feature(geometry: Point(Geographic(lon: 9, lat: 48))),
          Feature(geometry: Point(Geographic(lon: 9.5, lat: 48))),
        ],
        color: .literal(Colors.orange.withValues(alpha: 0.5)),
        radius: const .literal(20),
        strokeColor: const .literal(Colors.red),
        strokeWidth: const .literal(2),
      ),
    ],
  );
}

See the example app.

Markers

Markers

Markers show an icon, a label or both. Add the icon image to the style first:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
bool _imageLoaded = false;
final _points = <Feature<Point>>[
  const Feature(geometry: Point(Geographic(lon: 9.17, lat: 47.68))),
  const Feature(geometry: Point(Geographic(lon: 9, lat: 48))),
];

@override
Widget build(BuildContext context) {
  return MapLibreMap(
    options: MapOptions(initCamera: .position(center: Geographic(lon: 9.17, lat: 47.68), zoom: 7)),
    onStyleLoaded: (style) async {
      await style.addImageFromIconData(
        id: 'marker',
        iconData: Icons.location_on,
        color: Colors.red,
      );
      setState(() => _imageLoaded = true);
    },
    // add a marker where the user taps
    onClick: (event) =>
        setState(() => _points.add(Feature(geometry: Point(event.point)))),
    layers: [
      MarkerLayer(
        points: _points,
        textField: const .literal('Marker'),
        textAllowOverlap: const .literal(true),
        iconImage: _imageLoaded ? const .literal('marker') : null,
        iconAnchor: const .literal(IconAnchor.bottom),
        textOffset: const .literal(Offset(0, 1)),
      ),
    ],
  );
}

See the example app.

Polygons

Polygons

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@override
Widget build(BuildContext context) {
  return MapLibreMap(
    options: MapOptions(initCamera: .position(center: Geographic(lon: 9.17, lat: 47.68), zoom: 7)),
    layers: [
      PolygonLayer(
        polygons: [
          Feature(
            geometry: Polygon.from(const [
              [
                Geographic(lon: 8.2013, lat: 48.1074),
                Geographic(lon: 8.8853, lat: 48.0943),
                Geographic(lon: 8.7597, lat: 47.6933),
                Geographic(lon: 9.6320, lat: 48.0893),
                Geographic(lon: 8.6854, lat: 48.4538),
                Geographic(lon: 8.2013, lat: 48.1074),
              ],
            ]),
          ),
        ],
        color: .literal(Colors.lightBlueAccent.withValues(alpha: 0.6)),
        outlineColor: const .literal(Colors.blue),
      ),
    ],
  );
}

See the example app.

Polylines

Polylines

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@override
Widget build(BuildContext context) {
  return MapLibreMap(
    options: MapOptions(initCamera: .position(center: Geographic(lon: 9.17, lat: 47.68), zoom: 7)),
    layers: [
      PolylineLayer(
        polylines: [
          Feature(
            geometry: LineString.from(const [
              Geographic(lon: 9.17, lat: 47.68),
              Geographic(lon: 9.5, lat: 48),
              Geographic(lon: 9, lat: 48),
            ]),
          ),
        ],
        color: const .literal(Colors.red),
        width: const .literal(4),
        blur: const .literal(3),
        dashArray: const .literal([5, 5]),
      ),
    ],
  );
}

See the example app.

Style each feature

Every style parameter is an expression, so it can depend on the properties of each feature:

CircleLayer(
  points: points,
  radius: .get('size').asNumber(),
  color: .get('color').convertToColor(),
)

A map can show any number of annotation layers, each with its own style. For properties that the annotation layers don't have, use the style layer they are built on.

Interaction