Maps, Location & Device Features

Drawing Polylines & Polygons

15 min Lesson 4 of 12

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.

Note: Every 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)
  • pointsList<LatLng> of coordinates (required)
  • color — stroke colour (default: black)
  • width — stroke width in screen pixels (default: 10)
  • patterns — dashed / dotted patterns via PatternItem
  • geodesic — whether to follow the Earth’s curvature between points
  • visible — 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 colour
  • strokeWidth — border thickness in screen pixels
  • holes — list of List<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.

Tip: Use 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'))
Warning: 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: false if you do not need great-circle interpolation — it is cheaper to compute.
  • Prefer const constructors for PolylineId and PolygonId to 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.