FlutterFlow has built-in Firebase Cloud Messaging support. Enable it in Settings > Push Notifications, upload your Apple APNs authentication key for iOS, add the Request Notification Permission action to your onboarding flow, store the FCM token in the user's Firestore document, then test by sending from the Firebase Console. iOS notifications silently fail if you skip the APNs key upload.
Push Notifications from Zero to Working
Push notifications are one of the highest-ROI features in any mobile app — but the setup involves multiple platforms, keys, and configuration steps that beginners often get partially right. This guide walks through the exact sequence: enabling FCM in FlutterFlow, configuring the Apple Push Notification service for iOS, wiring up the permission flow in your app, and verifying everything works end-to-end before you write a single line of custom code.
Prerequisites
- FlutterFlow project with Firebase connected (Settings > Firebase > Connect)
- Apple Developer account ($99/yr) if targeting iOS
- Firebase project with iOS and Android apps registered
- Basic familiarity with Firestore collections and documents
Step-by-step guide
Enable Push Notifications in FlutterFlow Settings
Enable Push Notifications in FlutterFlow Settings
Open your FlutterFlow project and navigate to Settings (gear icon in the left panel) > Notifications. Toggle 'Enable Push Notifications' to on. FlutterFlow will automatically add the firebase_messaging package to your project and generate the required AndroidManifest.xml changes for Android. For iOS, you also need to add the Push Notifications capability — FlutterFlow handles this in the generated Xcode project. Once enabled, a confirmation banner appears and the toggle stays green. This step takes about 30 seconds and has no cost on any FlutterFlow plan.
Expected result: The Push Notifications toggle is green and FlutterFlow shows 'Push Notifications Enabled' status.
Create and Upload Your APNs Authentication Key for iOS
Create and Upload Your APNs Authentication Key for iOS
iOS notifications require an Apple Push Notification service key uploaded to Firebase. In Apple Developer Portal, go to Certificates, Identifiers & Profiles > Keys > click the + button. Name the key, check 'Apple Push Notifications service (APNs)', and click Continue then Register. Download the .p8 key file — you can only download it once. Note your Key ID and Team ID (visible in Membership section). In Firebase Console, go to Project Settings > Cloud Messaging > Apple app configuration and upload the .p8 file along with the Key ID and Team ID. Back in FlutterFlow Settings > Notifications, confirm the APNs status shows as configured.
Expected result: Firebase Console shows a green checkmark next to your iOS app in the Cloud Messaging settings page.
Add Request Permission Action to Your Onboarding Flow
Add Request Permission Action to Your Onboarding Flow
Users must grant notification permission before your app can deliver any notifications. Find the page in your app where it makes the most sense to ask — typically the end of onboarding or when a feature that requires notifications is first used. Select a Button widget, open its On Tap action flow, and click Add Action. Search for 'Request Notification' and select 'Request Notification Permission' from the FlutterFlow Actions list. Add a conditional branch after it: if permission is Granted, proceed normally; if Denied, show an informational alert explaining why notifications matter and offering to open Settings. Avoid asking for notification permission on first app launch before the user understands your app's value.
Expected result: Running the app shows the native iOS or Android permission dialog. Granting permission returns 'Granted' in the action output.
Store the FCM Token in the User's Firestore Document
Store the FCM Token in the User's Firestore Document
The FCM token is the device address you need to send targeted notifications. After the user grants permission, retrieve and store this token so your backend can find it later. In the same action flow, after the permission grant branch, add a Custom Action (or use the Get FCM Token action if visible in your FlutterFlow version). Then add an Update Document action targeting the current user's document in your 'users' Firestore collection. Set the field 'fcm_token' to the output of the Get FCM Token action. Also store 'fcm_token_updated_at' as the current timestamp so you can detect stale tokens. Do this on every app launch — tokens can rotate and you always want the freshest one.
1import 'package:firebase_messaging/firebase_messaging.dart';2import 'package:cloud_firestore/cloud_firestore.dart';3import 'package:firebase_auth/firebase_auth.dart';45Future<void> saveFcmToken() async {6 final String? token = await FirebaseMessaging.instance.getToken();7 final String? userId = FirebaseAuth.instance.currentUser?.uid;89 if (token == null || userId == null) return;1011 await FirebaseFirestore.instance12 .collection('users')13 .doc(userId)14 .update({15 'fcm_token': token,16 'fcm_token_updated_at': FieldValue.serverTimestamp(),17 'platform': Theme.of(navigatorKey.currentContext!).platform.name,18 });1920 // Listen for token refresh21 FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {22 FirebaseFirestore.instance23 .collection('users')24 .doc(userId)25 .update({'fcm_token': newToken});26 });27}Expected result: The logged-in user's Firestore document now has an fcm_token field containing a long alphanumeric string starting with the device identifier.
Send a Test Notification from Firebase Console
Send a Test Notification from Firebase Console
Before writing any backend code, confirm the entire chain works using the Firebase Console. Go to Firebase Console > Engage > Messaging > Send your first message. Enter a notification title and body text. Click 'Send test message' (not the full campaign flow). Paste the FCM token you saved in the previous step into the test dialog and click Test. The notification should appear on the device within 5-10 seconds. On iOS, make sure your device has granted notification permission and that your APNs key was uploaded correctly. On Android, make sure the app is not in the foreground (background or closed for the system tray notification to show).
Expected result: The notification appears in the device's notification shade with the exact title and body you typed in the Firebase Console.
Complete working example
1import 'package:firebase_messaging/firebase_messaging.dart';2import 'package:cloud_firestore/cloud_firestore.dart';3import 'package:firebase_auth/firebase_auth.dart';45// ─── Background Message Handler (top-level function) ────────────────────────6// Must be a top-level function, not a class method7@pragma('vm:entry-point')8Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {9 // Handle background messages here10 // Do not access BuildContext from here11 print('Background message received: ${message.messageId}');12}1314// ─── Main Initialization ─────────────────────────────────────────────────────1516Future<void> initPushNotifications() async {17 // Register background handler before calling Firebase.initializeApp18 FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);1920 // Request permission (iOS — Android 13+ also needs this)21 final NotificationSettings settings =22 await FirebaseMessaging.instance.requestPermission(23 alert: true,24 announcement: false,25 badge: true,26 carPlay: false,27 criticalAlert: false,28 provisional: false,29 sound: true,30 );3132 if (settings.authorizationStatus == AuthorizationStatus.authorized) {33 await saveFcmToken();34 }3536 // Handle notification taps when app was terminated37 final RemoteMessage? initialMessage =38 await FirebaseMessaging.instance.getInitialMessage();39 if (initialMessage != null) {40 _routeFromNotification(initialMessage.data);41 }4243 // Handle notification taps when app was in background44 FirebaseMessaging.onMessageOpenedApp.listen(_routeFromNotification);45}4647// ─── Token Storage ────────────────────────────────────────────────────────────4849Future<void> saveFcmToken() async {50 final String? token = await FirebaseMessaging.instance.getToken();51 final String? userId = FirebaseAuth.instance.currentUser?.uid;52 if (token == null || userId == null) return;5354 await FirebaseFirestore.instance.collection('users').doc(userId).update({55 'fcm_token': token,56 'fcm_token_updated_at': FieldValue.serverTimestamp(),57 });5859 FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {60 FirebaseFirestore.instance61 .collection('users')62 .doc(userId)63 .update({'fcm_token': newToken});64 });65}6667// ─── Navigation Routing ───────────────────────────────────────────────────────6869void _routeFromNotification(Map<String, dynamic> data) {70 final String page = data['target_page'] ?? '';71 if (page.isNotEmpty) {72 // Set FFAppState navigation target73 FFAppState().pendingNavPage = page;74 FFAppState().pendingNavId = data['id'] ?? '';75 }76}Common mistakes when adding Push Notifications to Your FlutterFlow Project
Why it's a problem: Forgetting to upload the APNs key for iOS — notifications silently fail
How to avoid: Upload the .p8 APNs key to Firebase Console > Project Settings > Cloud Messaging > Apple app configuration. Verify the Key ID and Team ID match exactly what is shown in your Apple Developer account.
Why it's a problem: Saving the FCM token only once at sign-up instead of on every app launch
How to avoid: Call saveFcmToken() every time the user opens the app when authenticated, and listen to FirebaseMessaging.instance.onTokenRefresh to update Firestore whenever the token changes.
Why it's a problem: Requesting notification permission on the very first screen of the app
How to avoid: Ask for permission at a meaningful moment — after the user completes onboarding, enables a feature that needs alerts, or explicitly taps a 'Turn on notifications' button.
Best practices
- Always register the background message handler as a top-level function (not a class method) — FCM isolates background handling and class methods will throw errors.
- Store platform alongside the FCM token so your Cloud Function can send platform-specific APNS/Android configs in one payload.
- Use batch writes when sending notifications to multiple users — Cloud Functions can fan out to 500 tokens per batch operation efficiently.
- Add a 'notifications_enabled' boolean to the user document so users can opt out from inside your app without going to device settings.
- Test on a physical iOS device — the simulator cannot receive actual push notifications.
- Include a notification_id in your data payload so you can deduplicate and track delivery on the server side.
- Monitor FCM delivery rates in Firebase Console > Cloud Messaging > Reports to catch delivery issues before users complain.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app with Firebase. Show me step-by-step how to set up Firebase Cloud Messaging for both iOS and Android, request notification permission from the user at the right time, save and refresh the FCM token in Firestore, and handle notification taps when the app is closed versus backgrounded versus open.
In my FlutterFlow app, add an action flow on the main dashboard page that: 1) calls a Custom Action to get the FCM token, 2) updates the current user's Firestore document fcm_token field, 3) listens for token refresh events. Also add a Request Notification Permission action to the end of my onboarding wizard's last page.
Frequently asked questions
Why are push notifications working on Android but not iOS?
The most common cause is a missing or incorrectly uploaded APNs authentication key. Go to Firebase Console > Project Settings > Cloud Messaging and verify your iOS app shows a green checkmark with your APNs key. Also confirm the App ID in your Apple Developer account has the Push Notifications capability enabled.
Can I send push notifications on the FlutterFlow free plan?
Yes. Push notifications via Firebase Cloud Messaging are free on all FlutterFlow plans. FCM itself is free through Firebase. You only pay Firebase for Firestore reads/writes used to look up FCM tokens, which falls within the free Spark plan limits for small apps.
How do I send notifications to all users at once?
Query all user documents from Firestore where fcm_token is not empty, then use the Firebase Admin SDK in a Cloud Function to send in batches of 500 using sendEachForMulticast(). Avoid sending individual messages in a loop — it is slow and hits rate limits.
Why does my app show a notification when in the background but not when open?
FCM suppresses system tray notifications when the app is in the foreground. You must listen to the FirebaseMessaging.onMessage stream and show your own in-app UI — a banner widget, a badge update, or a snackbar — to notify the user when the app is active.
Do I need to handle notification permissions differently for Android 13+?
Yes. Android 13 introduced a runtime notification permission similar to iOS. Call FirebaseMessaging.instance.requestPermission() for both platforms — FlutterFlow's built-in Request Notification Permission action handles this automatically on both OS versions.
What happens if a user denies notification permission on iOS?
Once denied, the OS permission dialog will never appear again for your app. You must direct the user to Settings > Your App > Notifications to re-enable. Show a friendly in-app prompt explaining the value of notifications before triggering the permission request to maximize grant rates.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation