Build a bus tracking system using Firestore for live bus positions and FlutterFlowGoogleMap for visualization. Buses update their GPS coordinates every 10 seconds from a driver app. Passengers see bus markers moving along route polylines with ETA for each stop. A Custom Widget smoothly animates marker positions between updates instead of teleporting. Each stop shows the next approaching buses with estimated arrival times calculated from current position and remaining stops.
Building a Passenger-Facing Bus Tracking Map in FlutterFlow
Public transit riders want to know exactly where their bus is and when it will arrive. This tutorial builds a real-time tracking system where bus positions update every 10 seconds on a map, routes display as colored polylines, and each stop shows the next arriving buses with ETAs. A separate driver view shares GPS location automatically.
Prerequisites
- A FlutterFlow project with Firestore and Google Maps API key configured
- Google Maps for Flutter enabled in your FlutterFlow project settings
- Basic understanding of latitude/longitude coordinates
- A routes and stops data structure planned for your transit system
Step-by-step guide
Design the Firestore data model for buses, routes, and stops
Design the Firestore data model for buses, routes, and stops
Create a buses collection with fields: routeId (String), currentLat (Double), currentLng (Double), heading (Double, compass direction 0-360), speed (Double, km/h), lastUpdated (Timestamp), nextStopId (String), estimatedArrival (Timestamp), driverUserId (String), isActive (Boolean). Create a routes collection: name (String, e.g. 'Route 42'), color (String, hex code for map polyline), stops (Array of Maps with fields: stopId, name, lat, lng, order). Create a stops collection for detailed stop info: name (String), lat (Double), lng (Double), routeIds (String Array, which routes serve this stop).
Expected result: Firestore has buses with live GPS data, routes with ordered stop arrays, and a stops collection for passenger-facing info.
Build the passenger map page with bus markers and route polylines
Build the passenger map page with bus markers and route polylines
Create a TrackingMapPage with a FlutterFlowGoogleMap widget taking up the full screen. Set the initial center to your city center coordinates and zoom to 13. Add a Backend Query on routes to fetch all routes. For each route, draw a Polyline on the map using the stops array coordinates and the route's color. Add a Backend Query on buses where isActive is true with real-time enabled (disable Single Time Query). For each bus, place a Marker at currentLat/currentLng with a custom bus icon. The map shows all active buses moving along their colored routes in real-time.
Expected result: A full-screen map displays colored route polylines, stop markers, and live bus position markers that update in real-time.
Create the stop detail view with approaching buses and ETAs
Create the stop detail view with approaching buses and ETAs
When a user taps a stop marker on the map, open a BottomSheet showing the stop name and a ListView of approaching buses. Query buses where routeId matches any route serving this stop and isActive is true. For each bus, calculate ETA by counting remaining stops between the bus's nextStopId and the selected stop, multiplied by average inter-stop time (2-3 minutes). Display each approaching bus as a row: route name with color indicator, direction arrow, ETA text (e.g. '3 min', '11 min'), and the bus identifier. Sort by ETA ascending so the soonest bus shows first.
Expected result: Tapping a stop marker shows a bottom sheet listing approaching buses sorted by estimated arrival time.
Build the driver app view for sharing GPS location
Build the driver app view for sharing GPS location
Create a DriverPage accessible only to users with a driver role. On Page Load, start a Timer that fires every 10 seconds. Each tick executes a Custom Action using the geolocator package to get the device's current latitude, longitude, heading, and speed. Update the bus document matching the driver's userId with the new position, heading, speed, and a server timestamp for lastUpdated. Calculate nextStopId by finding the closest upcoming stop on the route based on current position and direction. Add a Start Shift / End Shift toggle that sets isActive to true or false. Show the current route name and next stop name on screen for the driver's reference.
1// Custom Action: updateBusPosition2import 'package:geolocator/geolocator.dart';3import 'package:cloud_firestore/cloud_firestore.dart';45Future<void> updateBusPosition(String busDocId) async {6 final position = await Geolocator.getCurrentPosition(7 desiredAccuracy: LocationAccuracy.high,8 );9 await FirebaseFirestore.instance10 .collection('buses')11 .doc(busDocId)12 .update({13 'currentLat': position.latitude,14 'currentLng': position.longitude,15 'heading': position.heading,16 'speed': position.speed * 3.6, // m/s to km/h17 'lastUpdated': FieldValue.serverTimestamp(),18 });19}Expected result: Drivers toggle their shift on, and the app automatically shares GPS position every 10 seconds to Firestore.
Animate bus marker movement for smooth visual tracking
Animate bus marker movement for smooth visual tracking
Create a Custom Widget that wraps the map marker logic. Instead of instantly jumping the bus icon to new coordinates every 10 seconds, interpolate between the old and new positions over the update interval. Use an AnimationController with a 10-second duration. When a new position arrives from Firestore, store the old lat/lng and animate a Tween from old to new coordinates. The marker slides smoothly across the map. Also rotate the bus icon based on the heading field so it points in the direction of travel. This makes the tracking feel fluid and professional rather than jerky.
Expected result: Bus markers glide smoothly between position updates instead of teleporting every 10 seconds.
Complete working example
1FIRESTORE DATA MODEL:2 buses/{busId}3 routeId: String4 currentLat: Double5 currentLng: Double6 heading: Double (0-360)7 speed: Double (km/h)8 lastUpdated: Timestamp9 nextStopId: String10 estimatedArrival: Timestamp11 driverUserId: String12 isActive: Boolean1314 routes/{routeId}15 name: String ('Route 42')16 color: String ('#FF5733')17 stops: [18 { stopId: 's1', name: 'Main St', lat: 40.71, lng: -74.01, order: 1 },19 { stopId: 's2', name: 'Park Ave', lat: 40.72, lng: -74.00, order: 2 }20 ]2122 stops/{stopId}23 name: String24 lat: Double25 lng: Double26 routeIds: [String]2728PAGE: TrackingMapPage (passenger view)29 Stack30 ├── FlutterFlowGoogleMap (full screen)31 │ Polylines: from routes[].stops coordinates, colored32 │ Markers: stop pins + live bus icons33 │ Bus markers: real-time query on buses, isActive==true34 │ Stop tap → open BottomSheet35 └── BottomSheet (stop detail)36 Text (stop name)37 ListView (approaching buses)38 Row: route color dot + route name + ETA text39 Sorted by ETA ascending4041PAGE: DriverPage (driver view)42 Column43 ├── Text (current route name)44 ├── Text (next stop name)45 ├── Toggle (Start Shift / End Shift → isActive)46 └── Timer (every 10s → Custom Action updateBusPosition)4748CUSTOM WIDGET: AnimatedBusMarker49 AnimationController (10s duration)50 On new position: animate Tween from oldLatLng → newLatLng51 Rotate icon by heading degrees5253ETA CALCULATION:54 remainingStops = stops between bus.nextStopId and selectedStop55 eta = remainingStops * avgInterStopTime (2.5 min)56 Display: '3 min' | '11 min' | 'Arriving'Common mistakes when creating a Real-Time Bus Tracking System in FlutterFlow
Why it's a problem: Teleporting bus markers between GPS updates instead of animating
How to avoid: Use an AnimationController in a Custom Widget to interpolate between old and new coordinates over the update interval for fluid marker movement.
Why it's a problem: Updating bus position on every frame or every second
How to avoid: Update every 10 seconds, which balances tracking accuracy with Firestore write costs. Animate between updates on the client for visual smoothness.
Why it's a problem: Not filtering inactive buses from the passenger map query
How to avoid: Always filter the bus query with isActive equals true. Drivers toggle isActive off when ending their shift.
Best practices
- Update bus GPS position every 10 seconds as a balance between accuracy and Firestore costs
- Animate marker movement between updates for smooth visual tracking
- Filter bus queries to isActive equals true so only in-service buses display
- Use route-colored polylines to distinguish different bus routes on the map
- Rotate bus marker icons based on heading for intuitive direction indication
- Calculate ETAs from remaining stops and average inter-stop time
- Show the nearest approaching buses sorted by ETA when a user taps a stop
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a real-time bus tracking system in FlutterFlow with Google Maps showing live bus positions, route polylines, stop markers, ETA calculations, and a driver app for sharing GPS location. Show me the Firestore data model, map setup, ETA logic, and the geolocator Custom Action for the driver view.
Create a full-screen Google Map page. Add markers for bus stops and a bottom sheet that appears when tapping a stop marker, showing a list of approaching buses with arrival times.
Frequently asked questions
How accurate is GPS tracking on mobile devices?
Modern smartphones have GPS accuracy of 3-5 meters in open areas. In urban canyons with tall buildings, accuracy may degrade to 10-15 meters. The 10-second update interval smooths out minor position noise.
How much does real-time tracking cost in Firestore?
Each bus updating every 10 seconds produces 8,640 writes per day. For 20 buses that is about 173,000 writes per day. At Firestore pricing of $0.18 per 100K writes, that costs roughly $0.31 per day. Real-time listener reads are charged per initial load plus per change.
Can I show traffic conditions on the map?
Yes. FlutterFlowGoogleMap supports the traffic layer. Enable it in the map properties to show real-time traffic colors on roads. You can also use the Google Directions API for traffic-aware ETA calculations.
What happens when a bus loses internet connection?
The bus marker freezes at its last known position. Add a staleness check: if lastUpdated is more than 60 seconds old, show the marker as grey or faded to indicate the position may be outdated.
Can passengers get notifications when their bus is approaching?
Yes. Let passengers set an alert for a specific stop. A Cloud Function monitors bus positions and sends a push notification when the bus is one or two stops away from the selected stop.
Can RapidDev help build a transit tracking system?
Yes. RapidDev can implement GTFS feed integration, multi-modal transit support, accessibility features, real-time traffic-aware ETAs, and passenger analytics dashboards.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation