Use a Custom Widget wrapping GoogleMap from the google_maps_flutter package to render polylines for route paths, polygons for delivery zone boundaries, and circles for radius overlays. Store overlay coordinates in Firestore collections (routes, zones, locations). Show a custom info window Component via Bottom Sheet on marker tap. Detect geofencing by checking if a user LatLng falls inside a polygon using a point-in-polygon Custom Function.
Adding Route Paths, Delivery Zones, and Geofencing to Google Maps in FlutterFlow
Location-based apps need more than markers — they need route lines, area boundaries, radius circles, and geofencing. This tutorial builds a Custom Widget that overlays polylines, polygons, and circles on Google Maps, fetches overlay data from Firestore, and detects when users enter or exit defined zones.
Prerequisites
- A FlutterFlow project with Google Maps API key configured in Settings → Integrations → Google Maps
- Firestore enabled with collections for zones and routes
- FlutterFlow Pro plan for Custom Widget code
- Google Maps API enabled with Maps SDK for Android and iOS in Google Cloud Console
Step-by-step guide
Add google_maps_flutter to Pubspec and create the MapOverlayWidget Custom Widget
Add google_maps_flutter to Pubspec and create the MapOverlayWidget Custom Widget
Add google_maps_flutter: ^2.5.0 to Pubspec Dependencies. Create a Custom Widget named MapOverlayWidget with parameters: initialLat (double), initialLng (double), initialZoom (double, default 13). In the build method, return a GoogleMap widget with initialCameraPosition set from the parameters and empty sets for polylines, polygons, circles, and markers that you will populate in the next steps.
Expected result: A Google Map renders in the Custom Widget at the specified initial position with no overlays yet.
Fetch route coordinates from Firestore and render Polyline overlays
Fetch route coordinates from Firestore and render Polyline overlays
Create a Firestore routes collection with fields: name (String), points (Array of GeoPoint), color (String hex like '#FF0000'), width (int). In the Custom Widget initState, query the routes collection. For each document, convert the GeoPoint array to a List<LatLng> and create a Polyline object with polylineId: PolylineId(doc.id), points: latLngList, color: Color(hexValue), width: doc['width']. Add all Polylines to the GoogleMap polylines set.
Expected result: Route paths appear on the map as colored lines connecting the coordinate points from Firestore.
Add Polygon overlays for delivery zone boundaries from Firestore
Add Polygon overlays for delivery zone boundaries from Firestore
Create a Firestore zones collection with fields: name (String), boundary (Array of GeoPoint — at least 3 points), fillColor (String hex), strokeColor (String hex). In initState, query zones and convert each boundary array to List<LatLng>. Create Polygon objects with polygonId: PolygonId(doc.id), points: boundaryLatLngs, fillColor: Color(hexFill).withOpacity(0.2), strokeColor: Color(hexStroke), strokeWidth: 2. Add to the GoogleMap polygons set.
Expected result: Semi-transparent colored polygons outline delivery zone boundaries on the map.
Add Circle overlays for radius display and handle marker tap for custom info window
Add Circle overlays for radius display and handle marker tap for custom info window
For circle overlays, add Circle objects with center: LatLng, radius: double (meters), fillColor with opacity 0.15, strokeWidth: 1. For markers, create a Marker for each location with onTap callback. On tap, call a callback to the parent page via Action Parameter passing the location document ID. The parent page then triggers a Show Bottom Sheet action displaying a LocationDetail Component with the location's name, address, and distance. This replaces the default info window with a fully styled Component.
Expected result: Blue circles show radius around key locations, and tapping a marker opens a styled Bottom Sheet with location details.
Implement point-in-polygon geofencing detection with a Custom Function
Implement point-in-polygon geofencing detection with a Custom Function
Create a Custom Function isInsideZone that takes userLat (double), userLng (double), and polygonPoints (List<LatLng>) parameters. Implement the ray-casting algorithm: cast a horizontal ray from the point and count polygon edge intersections — odd count means inside. Call this function whenever the user's location updates (from FlutterFlow's location stream or a periodic check). If the function returns true for a zone, trigger a notification or update a Page State insideZoneName variable displayed on the map.
Expected result: The app detects when the user enters or exits a delivery zone and displays the zone name or triggers an alert.
Complete working example
1// Custom Widget: MapOverlayWidget2// Pubspec: google_maps_flutter: ^2.5.034import 'package:flutter/material.dart';5import 'package:google_maps_flutter/google_maps_flutter.dart';6import 'package:cloud_firestore/cloud_firestore.dart';78class MapOverlayWidget extends StatefulWidget {9 final double width;10 final double height;11 final double initialLat;12 final double initialLng;13 final double initialZoom;14 final Future Function(String locationId)? onMarkerTap;1516 const MapOverlayWidget({17 Key? key,18 required this.width,19 required this.height,20 this.initialLat = 37.7749,21 this.initialLng = -122.4194,22 this.initialZoom = 13,23 this.onMarkerTap,24 }) : super(key: key);2526 @override27 State<MapOverlayWidget> createState() => _MapOverlayWidgetState();28}2930class _MapOverlayWidgetState extends State<MapOverlayWidget> {31 Set<Polyline> _polylines = {};32 Set<Polygon> _polygons = {};33 Set<Circle> _circles = {};34 Set<Marker> _markers = {};3536 @override37 void initState() {38 super.initState();39 _loadOverlays();40 }4142 Future<void> _loadOverlays() async {43 final firestore = FirebaseFirestore.instance;4445 // Load polylines from routes collection46 final routesDocs = await firestore.collection('routes').get();47 final polylines = routesDocs.docs.map((doc) {48 final points = (doc['points'] as List)49 .map((gp) => LatLng(gp.latitude, gp.longitude))50 .toList();51 return Polyline(52 polylineId: PolylineId(doc.id),53 points: points,54 color: _hexToColor(doc['color'] ?? '#4285F4'),55 width: doc['width'] ?? 4,56 );57 }).toSet();5859 // Load polygons from zones collection60 final zonesDocs = await firestore.collection('zones').get();61 final polygons = zonesDocs.docs.map((doc) {62 final boundary = (doc['boundary'] as List)63 .map((gp) => LatLng(gp.latitude, gp.longitude))64 .toList();65 return Polygon(66 polygonId: PolygonId(doc.id),67 points: boundary,68 fillColor: _hexToColor(doc['fillColor'] ?? '#4285F4').withOpacity(0.2),69 strokeColor: _hexToColor(doc['strokeColor'] ?? '#4285F4'),70 strokeWidth: 2,71 );72 }).toSet();7374 setState(() {75 _polylines = polylines;76 _polygons = polygons;77 });78 }7980 Color _hexToColor(String hex) {81 hex = hex.replaceAll('#', '');82 if (hex.length == 6) hex = 'FF$hex';83 return Color(int.parse(hex, radix: 16));84 }8586 // Point-in-polygon (ray casting) for geofencing87 static bool isInsideZone(double lat, double lng, List<LatLng> polygon) {88 bool inside = false;89 int j = polygon.length - 1;90 for (int i = 0; i < polygon.length; i++) {91 if ((polygon[i].latitude > lat) != (polygon[j].latitude > lat) &&92 lng < (polygon[j].longitude - polygon[i].longitude) *93 (lat - polygon[i].latitude) /94 (polygon[j].latitude - polygon[i].latitude) +95 polygon[i].longitude) {96 inside = !inside;97 }98 j = i;99 }100 return inside;101 }102103 @override104 Widget build(BuildContext context) {105 return SizedBox(106 width: widget.width,107 height: widget.height,108 child: GoogleMap(109 initialCameraPosition: CameraPosition(110 target: LatLng(widget.initialLat, widget.initialLng),111 zoom: widget.initialZoom,112 ),113 polylines: _polylines,114 polygons: _polygons,115 circles: _circles,116 markers: _markers,117 ),118 );119 }120}Common mistakes when creating a Custom Map Overlay for Location-Based Services in FlutterFlow
Why it's a problem: Drawing hundreds of polyline points without simplification on mobile
How to avoid: Simplify paths using a Douglas-Peucker algorithm before rendering — reduce point count while preserving the visual shape of the route.
Why it's a problem: Using the built-in FlutterFlowGoogleMap widget and expecting polyline and polygon support
How to avoid: Create a Custom Widget with google_maps_flutter so you have full access to all overlay types.
Why it's a problem: Storing zone boundaries as a flat array of numbers instead of GeoPoint array
How to avoid: Use Firestore GeoPoint type for each coordinate. The GeoPoint array preserves lat/lng as a single object per point.
Best practices
- Store overlay coordinates in Firestore so zones and routes can be updated without code changes
- Use Polygon fillColor with low opacity (0.15-0.25) so the underlying map remains visible
- Set polyline width between 3-6 pixels — too thin is invisible, too thick obscures the map
- Show a custom Bottom Sheet Component on marker tap instead of the default info window for full styling control
- Implement the ray-casting algorithm as a reusable Custom Function for geofencing checks across pages
- Add a map legend overlay Container in the top corner explaining what each color represents
- Limit the initial query to overlays within the visible map bounds to reduce Firestore reads
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to add polylines for routes, polygons for delivery zones, and circles for radius overlays to Google Maps in FlutterFlow. Show me a Custom Widget using google_maps_flutter with Firestore data loading, overlay rendering, and point-in-polygon geofencing.
Create a full-screen Google Map with colored route lines between points, shaded area boundaries for delivery zones, and a bottom sheet that opens when I tap a marker.
Frequently asked questions
Can I draw a route between two addresses without manually specifying every coordinate?
Yes. Use the Google Directions API via an API Call to get the encoded polyline string between origin and destination, then decode it into LatLng points using the polyline decoding algorithm.
How do I update delivery zone boundaries without redeploying the app?
Store zone boundaries as GeoPoint arrays in Firestore. Update the coordinates in the Firebase console or an admin dashboard, and the map reflects changes on the next load.
Does the geofencing detection work in the background when the app is closed?
No. The ray-casting Custom Function runs only while the app is active. For background geofencing, you need a native plugin like geofencing_api or platform-specific code after exporting the project.
Can I show a heatmap layer on the map?
Yes, but it requires the google_maps_flutter_heatmap package in a separate Custom Widget. FlutterFlow's built-in map does not support heatmaps.
How many polygon and polyline overlays can the map handle before performance degrades?
Google Maps handles 50-100 overlays smoothly. Beyond that, simplify paths, reduce point counts, and only load overlays within the visible map bounds.
Can RapidDev help build a location-based delivery or logistics app?
Yes. RapidDev can implement real-time driver tracking, dynamic zone management, route optimization, and geofence-triggered notifications for delivery and logistics platforms.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation