Drawing Polylines & Polygons
Drawing Polylines & Polygons
Google Maps Flutter lets you render vector overlays directly on the map. Two of the most useful overlay types are Polyline — a connected sequence of line segments — and Polygon — a closed, optionally filled shape. Both are defined by a list of LatLng coordinates and are drawn by passing them into the GoogleMap widget.
Understanding the Overlay Model
Overlays in the Maps SDK are managed as Sets: you maintain a Set<Polyline> or Set<Polygon> in your widget state, and pass those sets to the corresponding polylines / polygons parameters of GoogleMap. Each overlay is identified by a unique PolylineId or PolygonId. When you call setState with an updated set, the map re-renders only the changed overlays — making incremental updates efficient.
Polyline and Polygon requires a unique ID within its set. Re-using the same ID replaces the previous overlay silently, which is actually the correct pattern for updating an existing overlay.Drawing a Polyline
A Polyline connects a list of LatLng points with straight segments. Key configuration properties include:
polylineId— unique identifier (required)points—List<LatLng>of coordinates (required)color— stroke colour (default: black)width— stroke width in screen pixels (default: 10)patterns— dashed / dotted patterns viaPatternItemgeodesic— whether to follow the Earth’s curvature between pointsvisible— toggle visibility without removing from the set
Polyline Example — Route Between Two Cities
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class RouteMapPage extends StatefulWidget {
const RouteMapPage({super.key});
@override
State<RouteMapPage> createState() => _RouteMapPageState();
}
class _RouteMapPageState extends State<RouteMapPage> {
final Set<Polyline> _polylines = {};
static const CameraPosition _initial = CameraPosition(
target: LatLng(24.7136, 46.6753), // Riyadh
zoom: 6,
);
@override
void initState() {
super.initState();
_buildRoute();
}
void _buildRoute() {
final List<LatLng> routePoints = const [
LatLng(24.7136, 46.6753), // Riyadh
LatLng(23.8859, 45.0792), // Al Dawadmi
LatLng(22.3372, 43.1225), // Bisha
LatLng(21.4858, 39.1925), // Jeddah
];
setState(() {
_polylines.add(
Polyline(
polylineId: const PolylineId('riyadh_to_jeddah'),
points: routePoints,
color: Colors.blue,
width: 5,
geodesic: true,
),
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Route Map')),
body: GoogleMap(
initialCameraPosition: _initial,
polylines: _polylines,
),
);
}
}
Drawing a Polygon
A Polygon is a closed shape whose last point is implicitly connected back to the first. In addition to the properties shared with Polyline, polygons support:
fillColor— interior fill colour (use an alpha value for transparency)strokeColor— border colourstrokeWidth— border thickness in screen pixelsholes— list ofList<LatLng>defining cut-outs inside the polygon
Polygon Example — Highlight a Neighbourhood
class ZoneMapPage extends StatefulWidget {
const ZoneMapPage({super.key});
@override
State<ZoneMapPage> createState() => _ZoneMapPageState();
}
class _ZoneMapPageState extends State<ZoneMapPage> {
final Set<Polygon> _polygons = {};
@override
void initState() {
super.initState();
_addZone();
}
void _addZone() {
const List<LatLng> zonePoints = [
LatLng(25.2048, 55.2708),
LatLng(25.2060, 55.2780),
LatLng(25.1990, 55.2810),
LatLng(25.1960, 55.2730),
];
setState(() {
_polygons.add(
Polygon(
polygonId: const PolygonId('delivery_zone_1'),
points: zonePoints,
strokeColor: Colors.red,
strokeWidth: 3,
fillColor: Colors.red.withOpacity(0.25),
),
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Delivery Zones')),
body: GoogleMap(
initialCameraPosition: const CameraPosition(
target: LatLng(25.2048, 55.2708),
zoom: 14,
),
polygons: _polygons,
),
);
}
}
Combining Polylines and Polygons
You can pass both polylines and polygons to the same GoogleMap widget simultaneously. A common real-world pattern is to show a route polyline alongside delivery-zone polygons, or to draw a geofence boundary (polygon) with a path history (polyline) inside it.
Colors.someColor.withOpacity(0.3) for fillColor so the map tiles remain visible beneath the polygon. A fully opaque fill blocks the underlying map completely.Updating and Removing Overlays
Because overlays are stored in a Set, you can add, replace, or remove them at any time:
- Add: call
setState(() => _polylines.add(...)) - Replace: remove the old entry by ID, then add the updated one, all inside one
setState - Remove:
setState(() => _polylines.removeWhere((p) => p.polylineId.value == 'my_id'))
Polyline and Polygon objects are immutable after construction. You cannot change a property in-place. Always create a new instance with the updated values and replace the old one in the set inside setState.Performance Considerations
Rendering many points in a single polyline or polygon can degrade frame rate on low-end devices. Apply these strategies when working with large datasets:
- Use the Ramer-Douglas-Peucker algorithm to simplify dense coordinate lists before rendering.
- Split very long routes into multiple shorter polylines so you can load only visible segments.
- Set
geodesic: falseif you do not need great-circle interpolation — it is cheaper to compute. - Prefer
constconstructors forPolylineIdandPolygonIdto avoid unnecessary object allocation on each rebuild.
Summary
Polyline and Polygon overlays give you precise control over route visualisation and zone demarcation on a Google Map. Both are driven by a List<LatLng> and configured through colour, width, and fill properties. Manage them in a Set held in widget state, update them immutably by replacement, and optimise large coordinate sets with simplification algorithms. In the next lesson you will add interactive tap handlers to these overlays so users can select and inspect them.