Multiple Geometries in Flutter
This tutorial shows how to display markers, polylines, polygons, and circles all on the same map.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Complete Example
Display all geometry types together on a single map:
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class MultipleGeometriesScreen extends StatefulWidget {
@override
_MultipleGeometriesScreenState createState() => _MultipleGeometriesScreenState();
}
class _MultipleGeometriesScreenState extends State<MultipleGeometriesScreen> {
MapController? mapController;
bool showMarkers = true;
bool showPolylines = true;
bool showPolygons = true;
bool showCircles = true;
// --- Markers (widget-based, from mapChildren) ---
final List<Marker> markers = [
Marker(
point: Position(2.2945, 48.8584), // Eiffel Tower
size: const Size(32, 32),
alignment: Alignment.bottomCenter,
child: const Icon(Icons.location_on, color: Colors.red, size: 32),
),
Marker(
point: Position(2.3376, 48.8606), // Louvre
size: const Size(32, 32),
alignment: Alignment.bottomCenter,
child: const Icon(Icons.location_on, color: Colors.blue, size: 32),
),
Marker(
point: Position(2.3499, 48.8530), // Notre-Dame
size: const Size(32, 32),
alignment: Alignment.bottomCenter,
child: const Icon(Icons.location_on, color: Colors.green, size: 32),
),
Marker(
point: Position(2.3431, 48.8867), // Sacré-Cœur
size: const Size(32, 32),
alignment: Alignment.bottomCenter,
child: const Icon(Icons.location_on, color: Colors.orange, size: 32),
),
];
// --- Polylines (style layer) ---
final List<LineString> polylines = [
LineString(
coordinates: [
Position(2.2945, 48.8584), // Eiffel Tower
Position(2.3376, 48.8606), // Louvre
Position(2.3499, 48.8530), // Notre-Dame
],
),
LineString(
coordinates: [
Position(2.3499, 48.8530), // Notre-Dame
Position(2.3640, 48.8670), // Midpoint
Position(2.3431, 48.8867), // Sacré-Cœur
],
),
];
// --- Polygons (style layer) ---
final List<Polygon> polygons = [
Polygon(
coordinates: [
[
Position(2.340, 48.855),
Position(2.360, 48.855),
Position(2.360, 48.845),
Position(2.340, 48.845),
Position(2.340, 48.855), // ring must close
],
],
),
Polygon(
coordinates: [
[
Position(2.350, 48.862),
Position(2.370, 48.862),
Position(2.370, 48.852),
Position(2.350, 48.852),
Position(2.350, 48.862),
],
],
),
];
// --- Circles (style layer) ---
final List<Point> circlePoints = [
Point(coordinates: Position(2.2945, 48.8584)), // Eiffel Tower area
Point(coordinates: Position(2.3431, 48.8867)), // Sacré-Cœur area
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Multiple Geometries')),
body: Column(
children: [
// Toggle buttons
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
color: Colors.grey[100],
child: Row(
children: [
_toggleChip('Markers', showMarkers, (v) => setState(() => showMarkers = v)),
SizedBox(width: 4),
_toggleChip('Lines', showPolylines, (v) => setState(() => showPolylines = v)),
SizedBox(width: 4),
_toggleChip('Polygons', showPolygons, (v) => setState(() => showPolygons = v)),
SizedBox(width: 4),
_toggleChip('Circles', showCircles, (v) => setState(() => showCircles = v)),
],
),
),
// Map
Expanded(
child: MapMetricsView(
options: MapOptions(
initStyle: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/YOUR_STYLE.json&token=YOUR_API_KEY',
initCenter: Position(2.3300, 48.8600), // lng, lat
initZoom: 13.0,
),
onMapCreated: (controller) => mapController = controller,
layers: [
if (showPolylines)
PolylineLayer(polylines: polylines, color: Colors.blue, width: 3),
if (showPolygons)
PolygonLayer(
polygons: polygons,
color: Colors.green.withOpacity(0.15),
outlineColor: Colors.green,
),
if (showCircles)
CircleLayer(
points: circlePoints,
radius: 20,
color: Colors.red.withOpacity(0.15),
strokeWidth: 2,
strokeColor: Colors.red,
),
],
mapChildren: [
if (showMarkers) WidgetLayer(markers: markers),
],
),
),
],
),
);
}
Widget _toggleChip(String label, bool value, ValueChanged<bool> onChanged) {
return FilterChip(
label: Text(label, style: TextStyle(fontSize: 12)),
selected: value,
onSelected: onChanged,
selectedColor: Colors.blue[100],
checkmarkColor: Colors.blue,
visualDensity: VisualDensity.compact,
);
}
}Geometry Types Summary
| Type | How it's rendered | Key Properties |
|---|---|---|
| Marker | Marker in WidgetLayer (mapChildren) — a real Flutter widget positioned over the map | point, size, child, alignment, rotate, flat |
| Polyline | PolylineLayer (layers), built from LineString geometries | polylines, color, width, dashArray |
| Polygon | PolygonLayer (layers), built from Polygon geometries | polygons, color, outlineColor |
| Circle | CircleLayer (layers), built from Point geometries | points, radius, color, strokeColor, strokeWidth |
There is no Marker/MarkerId/Polyline/PolylineId/Polygon/PolygonId/Circle/CircleId/InfoWindow/BitmapDescriptor API in this SDK. Point-based annotations come in two real flavors:
WidgetLayer+Marker— put inmapChildren, renders arbitrary Flutter widgets (icons, cards, custom shapes) positioned byPosition. Best when you need per-marker styling, taps, or custom widgets.MarkerLayer(inlayers) — a native style layer that renders a batch ofPointgeometries with a single sharediconImage/textFieldstyle. Cheaper for large numbers of uniformly-styled points, but all points in oneMarkerLayershare the same paint properties.
PolylineLayer, PolygonLayer, and CircleLayer are always native style layers built from geotypes geometry objects (LineString, Polygon, Point) — there's no widget-based equivalent for lines/polygons/circles.
Next Steps
- Measure Distances — Tap-driven polyline + distance calculation
- Popup on Click — Interactive markers with
WidgetLayer - Navigation Controls — Camera and gesture handling
Tip: Toggle a layers:/mapChildren: entry in and out of the list (as shown with the if (showX) guards above) to hide a layer without discarding the underlying data.