Location-based push notifications in FlutterFlow require three components: a Custom Action using the geolocator package to get and store the user's location in Firestore, a Firebase Cloud Function that runs on a schedule or Firestore write trigger to query nearby users using GeoHash, and FCM (Firebase Cloud Messaging) to send the targeted push notification to those users.
Architecture of a Geolocation-Triggered Push System
There are two main approaches to location-based push: client-side geofencing (the device monitors its own position) and server-side geofencing (a Cloud Function queries all user locations against defined zones). This tutorial covers both. Client-side is better for individual user triggers (e.g., 'notify me when I arrive home'); server-side is better for campaigns (e.g., 'notify all users within 1km of this event'). FlutterFlow handles the UI and data storage; Firebase handles the notification delivery.
Prerequisites
- FlutterFlow project with Firebase connected (Firestore and Authentication enabled)
- Firebase project on the Blaze plan (Cloud Functions required)
- FCM (Firebase Cloud Messaging) enabled — FlutterFlow has built-in FCM support under Settings → Firebase
- Familiarity with FlutterFlow Action Flows and App State variables
Step-by-step guide
Enable FCM in FlutterFlow and Firestore Schema
Enable FCM in FlutterFlow and Firestore Schema
In FlutterFlow, go to Settings (gear icon, top-right) → Firebase → Push Notifications. Toggle 'Enable Push Notifications' on. FlutterFlow will automatically add the firebase_messaging package and generate the FCM token management code. In your Firestore users collection, you need three additional fields: 'fcmToken' (String, to store the device push token), 'geoHash' (String, for efficient radius queries), and 'lastLocation' (Map with 'lat' and 'lng' keys, both Double). Add these in the FlutterFlow Firestore schema editor. These fields will be populated by your Custom Action when the user grants location access.
Expected result: The FlutterFlow Push Notifications setting shows 'Enabled' and your Firestore User schema includes fcmToken, geoHash, and lastLocation fields.
Create the UpdateLocationAndToken Custom Action
Create the UpdateLocationAndToken Custom Action
Go to Custom Code → Custom Actions → '+'. Name it 'updateLocationAndToken'. Add the geolocator package in Settings → Pubspec Dependencies. This action requests location permission, gets the current coordinates, computes a GeoHash string (a compact encoding of lat/lng for efficient range queries), gets the current FCM token, and writes all three to the user's Firestore document. Call this action on app start (from your initial page's Action Flow on Page Load) and when the user taps a 'Update Location' button. On iOS, the app must also be configured with location usage descriptions in Info.plist — FlutterFlow handles this via the location permission settings in project configuration.
1// Custom Action: updateLocationAndToken2// Packages: geolocator, firebase_messaging, cloud_firestore, firebase_auth3// Return type: String45Future<String> updateLocationAndToken() async {6 // 1. Request location permission7 LocationPermission permission = await Geolocator.checkPermission();8 if (permission == LocationPermission.denied) {9 permission = await Geolocator.requestPermission();10 if (permission == LocationPermission.denied) {11 return 'Location permission denied';12 }13 }14 if (permission == LocationPermission.deniedForever) {15 return 'Location permission permanently denied — enable in Settings';16 }1718 // 2. Get current position19 final position = await Geolocator.getCurrentPosition(20 desiredAccuracy: LocationAccuracy.medium,21 );2223 // 3. Compute GeoHash (precision 6 = ~1km accuracy)24 final geoHash = GeoHasher().encode(25 position.longitude,26 position.latitude,27 precision: 6,28 );2930 // 4. Get FCM token31 final fcmToken = await FirebaseMessaging.instance.getToken() ?? '';3233 // 5. Update Firestore34 final uid = FirebaseAuth.instance.currentUser?.uid;35 if (uid == null) return 'User not authenticated';3637 await FirebaseFirestore.instance.collection('users').doc(uid).update({38 'fcmToken': fcmToken,39 'geoHash': geoHash,40 'lastLocation': {41 'lat': position.latitude,42 'lng': position.longitude,43 },44 'locationUpdatedAt': FieldValue.serverTimestamp(),45 });4647 return 'Location updated: ${position.latitude}, ${position.longitude}';48}Expected result: After the action runs, the user's Firestore document shows their current geoHash, fcmToken, and lastLocation coordinates.
Create Geofence Zones in Firestore
Create Geofence Zones in Firestore
In Firebase Console, create a new top-level Firestore collection called 'geofences'. Each document represents one geographic zone that triggers a notification. Each document needs: 'name' (String, human-readable), 'centerLat' (Number), 'centerLng' (Number), 'radiusKm' (Number), 'notificationTitle' (String), 'notificationBody' (String), and 'active' (Boolean). You can manage geofences directly from the Firebase Console or build an admin screen in FlutterFlow. The Cloud Function in the next step will query this collection to know which zones to check and what notification to send when a user is inside them.
Expected result: The Firestore geofences collection has at least one active document with center coordinates, radius, and notification text.
Deploy the Geofence Check Cloud Function
Deploy the Geofence Check Cloud Function
This Cloud Function runs on a schedule (every 15 minutes using Cloud Scheduler) or can be triggered by a Firestore write to the users collection. It iterates over active geofences, queries users whose stored coordinates fall within the geofence radius using a Haversine distance calculation, and sends FCM notifications to those users via their stored fcmToken. This server-side approach means the app does not need to be open for the notification to fire — the server does all the work. Deploy via Firebase CLI. The function uses the firebase-admin SDK (already available in Cloud Functions) and the firebase-admin FCM Messaging API.
1// Firebase Cloud Function: checkGeofences2// Scheduled: every 15 minutes3// Deploy: firebase deploy --only functions:checkGeofences45const functions = require('firebase-functions');6const admin = require('firebase-admin');78function haversineKm(lat1, lng1, lat2, lng2) {9 const R = 6371;10 const dLat = (lat2 - lat1) * Math.PI / 180;11 const dLng = (lng2 - lng1) * Math.PI / 180;12 const a = Math.sin(dLat / 2) ** 2 +13 Math.cos(lat1 * Math.PI / 180) *14 Math.cos(lat2 * Math.PI / 180) *15 Math.sin(dLng / 2) ** 2;16 return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));17}1819exports.checkGeofences = functions.pubsub20 .schedule('every 15 minutes')21 .onRun(async () => {22 const db = admin.firestore();23 const geofencesSnap = await db.collection('geofences')24 .where('active', '==', true).get();2526 const usersSnap = await db.collection('users')27 .where('fcmToken', '!=', '')28 .where('locationUpdatedAt', '>', new Date(Date.now() - 30 * 60 * 1000))29 .get();3031 const batch = [];32 geofencesSnap.forEach(geofence => {33 const g = geofence.data();34 usersSnap.forEach(user => {35 const u = user.data();36 if (!u.lastLocation) return;37 const dist = haversineKm(38 g.centerLat, g.centerLng,39 u.lastLocation.lat, u.lastLocation.lng40 );41 if (dist <= g.radiusKm && u.fcmToken) {42 batch.push(43 admin.messaging().send({44 token: u.fcmToken,45 notification: {46 title: g.notificationTitle,47 body: g.notificationBody,48 },49 data: { geofenceId: geofence.id },50 })51 );52 }53 });54 });5556 await Promise.allSettled(batch);57 return null;58 });Expected result: The Cloud Function deploys and runs every 15 minutes. Users with stored locations inside active geofences receive push notifications on their devices.
Test the End-to-End Flow
Test the End-to-End Flow
To test without waiting 15 minutes: (1) In FlutterFlow, add a button on your home page wired to the updateLocationAndToken Custom Action — verify the Firestore document updates. (2) In the Firebase Console, open Functions and use the 'Run' option to invoke checkGeofences manually. (3) Check Firebase Console → Messaging → Activity to see if the notification was sent. (4) On a physical device running your app, verify the notification appears in the system notification shade. For testing geofences near your physical location, temporarily set a geofence radius of 10km around your city center. Use the FlutterFlow Run Mode on a real device (not browser) to test location permission dialogs.
Expected result: A push notification appears on the test device within 15 minutes of the user's location being saved within a geofence boundary.
Complete working example
1// ============================================================2// FlutterFlow Location-Based Push — Custom Action3// ============================================================4// Packages: geolocator, firebase_messaging, cloud_firestore,5// firebase_auth, dart_geohash67Future<String> updateLocationAndToken() async {8 // 1. Check and request location permission9 LocationPermission permission = await Geolocator.checkPermission();10 if (permission == LocationPermission.denied) {11 permission = await Geolocator.requestPermission();12 }13 if (permission == LocationPermission.denied ||14 permission == LocationPermission.deniedForever) {15 return 'Location permission not granted';16 }1718 // 2. Get current GPS coordinates19 final position = await Geolocator.getCurrentPosition(20 desiredAccuracy: LocationAccuracy.medium,21 timeLimit: const Duration(seconds: 10),22 );2324 // 3. Encode as GeoHash (precision 6 = ~1.2km cells)25 final geoHash = GeoHasher().encode(26 position.longitude,27 position.latitude,28 precision: 6,29 );3031 // 4. Retrieve current FCM push token32 String fcmToken = '';33 try {34 fcmToken = await FirebaseMessaging.instance.getToken() ?? '';35 } catch (_) {}3637 // 5. Write location data to user's Firestore document38 final uid = FirebaseAuth.instance.currentUser?.uid;39 if (uid == null) return 'Not authenticated';4041 await FirebaseFirestore.instance42 .collection('users')43 .doc(uid)44 .set(45 {46 'fcmToken': fcmToken,47 'geoHash': geoHash,48 'lastLocation': {49 'lat': position.latitude,50 'lng': position.longitude,51 },52 'locationUpdatedAt': FieldValue.serverTimestamp(),53 },54 SetOptions(merge: true),55 );5657 return 'Location saved: ${position.latitude.toStringAsFixed(4)}, '58 '${position.longitude.toStringAsFixed(4)}';59}6061// Note: The Cloud Function (checkGeofences.js) runs server-side62// via Firebase Cloud Functions on a 15-minute schedule.63// See the tutorial step 4 for the complete Node.js Cloud Function code.Common mistakes
Why it's a problem: Querying ALL users' locations every 5 minutes in a Cloud Function
How to avoid: Use a GeoHash prefix query to narrow the query to users in adjacent GeoHash cells before doing the distance calculation. Also filter by 'locationUpdatedAt' to only check users who have updated their location in the last 30 minutes — inactive users do not need to be checked.
Why it's a problem: Not requesting 'Always Allow' location permission on iOS for background notifications
How to avoid: Request LocationPermission.always on iOS for background scenarios. Add NSLocationAlwaysAndWhenInUseUsageDescription and NSLocationAlwaysUsageDescription to Info.plist. Show an explanation dialog before requesting 'Always' permission explaining why background location is needed.
Why it's a problem: Sending duplicate notifications to the same user for the same geofence
How to avoid: Store a 'lastNotifiedAt' map in each geofence document (keyed by userId) and check if the user was already notified in the last X hours before sending. Alternatively, use a dedicated Firestore 'notifications_sent' collection to track what was sent.
Why it's a problem: Using the geolocator package without adding the Android and iOS permission strings
How to avoid: In FlutterFlow, go to Settings → App Details → Permissions and enable Location. This automatically adds the required platform permission strings. FlutterFlow handles both Android and iOS entries.
Best practices
- Always show a clear value proposition before requesting location permission — 'We use your location to notify you of nearby deals' increases acceptance rates significantly.
- Use LocationAccuracy.medium (not high) for background location to reduce battery drain — medium accuracy uses network positioning, which is less battery-intensive than GPS.
- Implement a quiet hours setting in your app so users can suppress notifications during certain hours even if they are inside a geofence.
- Store location update timestamps and only check recently active users in Cloud Functions — a user who has not opened the app in 30 days should not be included in geofence checks.
- Add a user-facing 'Nearby Alerts' toggle in your app settings so users can opt out of location-based notifications without revoking system location permission.
- Test push notifications on physical devices — FCM tokens and notification delivery are unreliable in emulators and the FlutterFlow browser preview.
- Rate-limit notifications per geofence per user — at most one notification per geofence per 24 hours prevents notification fatigue.
- Geofences with radius under 100 meters are unreliable due to GPS accuracy limitations — design experiences for 200m+ radius for consistent triggering.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a location-based push notification system for a FlutterFlow app. The system stores user GPS coordinates in Firestore with a GeoHash, and a Firebase Cloud Function checks whether users are within defined geofence zones every 15 minutes. Write the complete Cloud Function in Node.js that reads from a 'geofences' Firestore collection, calculates Haversine distance for each active user, and sends FCM notifications to users within the radius.
Write a FlutterFlow Custom Action in Dart called updateLocationAndToken that: (1) requests location permission using geolocator, (2) gets current GPS coordinates, (3) encodes them as a GeoHash with precision 6, (4) retrieves the FCM token from firebase_messaging, and (5) saves all fields to the user's Firestore document using merge:true.
Frequently asked questions
Does the user need to have the app open to receive location-based push notifications?
No. This tutorial's server-side approach stores the user's last known location in Firestore, and the Cloud Function sends FCM notifications based on that stored data. FCM delivers notifications even when the app is closed. The app only needs to update the stored location when it IS open — the server handles notification delivery independently.
How accurate does the stored location need to be for geofencing to work reliably?
For geofences with a 500m+ radius, network-based location (LocationAccuracy.medium) is sufficient. For sub-100m precision, you need GPS accuracy, which drains battery faster and takes longer to acquire. Design your geofences to be at least 200-300m in radius to work reliably with medium accuracy.
How do I prevent the Cloud Function from costing too much in Firestore reads?
Use GeoHash prefix queries to limit the users returned to only those in nearby GeoHash cells. Filter by 'locationUpdatedAt' to skip users inactive for more than 30 minutes. With these filters, a 10,000-user app might only scan 50-200 documents per function run instead of all 10,000.
Can I trigger a notification immediately when a user enters a zone instead of waiting 15 minutes?
Yes. Use a Firestore onCreate/onUpdate Cloud Function trigger instead of a scheduler. Trigger on writes to the user's 'lastLocation' field, then run the geofence check for that single user. This gives near-real-time triggering (within seconds) and is more efficient since you only check one user per trigger, not all users.
Does FlutterFlow's built-in push notification support work with this Cloud Function approach?
Yes. FlutterFlow's push notification setup configures FCM token management and iOS APNs certificates. Your Cloud Function uses the firebase-admin SDK to send to individual FCM tokens directly, bypassing the FlutterFlow notification UI. The tokens are the same ones FlutterFlow stores — they are just used directly in the Cloud Function.
What happens to the location data if the user denies location permission later?
The Custom Action returns an error string and does not update Firestore. The user's old location remains in their document. If your geofence check function filters by 'locationUpdatedAt' with a 30-minute window, the user's stale location will stop being checked automatically. No crash or error occurs.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation