Build a notification preferences page with Switch toggles for each category (orders, messages, promotions, system) crossed with each channel (push, email, in-app). Store all preferences as a nested map in the user's Firestore document and save them in a single Update Document call. Register the FCM token on login and store it in the user document. Cloud Functions check the user's preferences map before sending any notification.
Building Per-Category Per-Channel Notification Preferences in FlutterFlow
Users expect granular control over which notifications they receive and how. This tutorial builds a preferences page where users toggle push, email, and in-app notifications independently for each category, backed by Firestore and enforced by Cloud Functions.
Prerequisites
- A FlutterFlow project with Firestore and Firebase Authentication configured
- Firebase Cloud Messaging enabled in the Firebase console
- A users collection with a document per authenticated user
- Firebase Blaze plan for Cloud Functions deployment
Step-by-step guide
Add the notificationPreferences nested map structure to the Firestore user document
Add the notificationPreferences nested map structure to the Firestore user document
In the users collection, add a field notificationPreferences of type Map. The map has category keys (orders, messages, promotions, system), each containing a sub-map with channel keys (push, email, inApp) set to Boolean. Example: notificationPreferences.orders.push: true, notificationPreferences.orders.email: false, notificationPreferences.orders.inApp: true. Also add an fcmToken (String) field to the user document for push delivery. Set default values — all channels true for orders and messages, all false for promotions.
Expected result: Each user document has a notificationPreferences map with 12 boolean toggles (4 categories x 3 channels) and an fcmToken field.
Build the preferences page with section headers and Switch toggle rows
Build the preferences page with section headers and Switch toggle rows
Create a NotificationPreferencesPage. Use a SingleChildScrollView containing a Column. For each category (Orders, Messages, Promotions, System), add a Text section header (titleMedium, bold, padding top 24). Below each header, add three Rows in the ListTile pattern: Row containing a Column (channel name Text + subtitle Text describing what it controls) and a trailing Switch widget. Create Page State booleans for all 12 toggles (e.g., ordersPush, ordersEmail, ordersInApp). Initialize them On Page Load from the current user's notificationPreferences map via a Backend Query on the user document.
Expected result: The page displays four sections with three toggles each, pre-populated with the user's current preference values.
Save all 12 preferences in a single Firestore Update Document call
Save all 12 preferences in a single Firestore Update Document call
Add a Save Preferences Button at the bottom. On tap, build the complete notificationPreferences map from the 12 Page State booleans: { 'orders': { 'push': ordersPush, 'email': ordersEmail, 'inApp': ordersInApp }, 'messages': { ... }, 'promotions': { ... }, 'system': { ... } }. Execute a single Update Document on the current user's document setting the notificationPreferences field to this map. Show a SnackBar 'Preferences saved' on success. Writing the entire map at once avoids 12 separate Firestore writes.
Expected result: Tapping Save writes all preferences to Firestore in one operation and shows a success confirmation.
Register the FCM push token on login and store it in the user document
Register the FCM push token on login and store it in the user document
In the On Page Load action of the home page (or post-login action flow), add a Custom Action that calls FirebaseMessaging.instance.getToken() to retrieve the device FCM token. Write this token to the user document's fcmToken field. Also listen for token refresh with FirebaseMessaging.instance.onTokenRefresh and update the document when the token changes. This ensures the server always has the current device token for push delivery.
Expected result: The user document contains the current device's FCM token, updated automatically on token refresh.
Write a Cloud Function that checks preferences before sending notifications
Write a Cloud Function that checks preferences before sending notifications
Deploy a Firebase Cloud Function sendNotification(userId, category, channel, title, body). The function reads the user document, checks notificationPreferences[category][channel]. If true and channel is 'push': send via admin.messaging().send({ token: user.fcmToken, notification: { title, body } }). If channel is 'email': send via a mail service. If channel is 'inApp': write to a notifications subcollection on the user document. If the preference is false, skip silently. Call this function from other Cloud Functions or Firestore triggers when events occur.
Expected result: Notifications are only delivered through channels the user has enabled for that specific category.
Complete working example
1FIRESTORE DATA MODEL:2 users/{userId}3 displayName: String4 email: String5 fcmToken: String6 notificationPreferences: Map7 orders:8 push: true9 email: true10 inApp: true11 messages:12 push: true13 email: false14 inApp: true15 promotions:16 push: false17 email: false18 inApp: false19 system:20 push: true21 email: true22 inApp: true2324PAGE: NotificationPreferencesPage25 Page State: ordersPush, ordersEmail, ordersInApp,26 messagesPush, messagesEmail, messagesInApp,27 promotionsPush, promotionsEmail, promotionsInApp,28 systemPush, systemEmail, systemInApp (all Boolean)2930WIDGET TREE:31 Scaffold32 AppBar (title: "Notification Preferences")33 SingleChildScrollView34 Column (padding: 16)35 ├── Text "Orders" (titleMedium, bold)36 ├── _PrefRow("Push Notifications", "Order updates on your device", ordersPush)37 ├── _PrefRow("Email", "Order confirmations via email", ordersEmail)38 ├── _PrefRow("In-App", "Order updates in notification center", ordersInApp)39 ├── Divider40 ├── Text "Messages" (titleMedium, bold)41 ├── _PrefRow("Push", "New message alerts", messagesPush)42 ├── _PrefRow("Email", "Message digest emails", messagesEmail)43 ├── _PrefRow("In-App", "Messages in notification center", messagesInApp)44 ├── Divider45 ├── Text "Promotions" (titleMedium, bold)46 ├── _PrefRow("Push", "Sale and discount alerts", promotionsPush)47 ├── _PrefRow("Email", "Promotional emails", promotionsEmail)48 ├── _PrefRow("In-App", "Promo banners in app", promotionsInApp)49 ├── Divider50 ├── Text "System" (titleMedium, bold)51 ├── _PrefRow("Push", "Security and maintenance alerts", systemPush)52 ├── _PrefRow("Email", "Account security emails", systemEmail)53 ├── _PrefRow("In-App", "System notices in app", systemInApp)54 ├── SizedBox (height: 24)55 └── Button "Save Preferences" (full width, primary)56 On Tap → Update Document users/{currentUser}57 notificationPreferences: { full map from Page State }58 → Show SnackBar "Preferences saved"5960_PrefRow Pattern:61 Row (padding: 12 0)62 ├── Expanded Column63 │ ├── Text (channel name, bodyLarge)64 │ └── Text (description, bodySmall, grey)65 └── Switch (bound to Page State boolean)66 On Changed → Update Page State6768ON PAGE LOAD:69 1. Read current user document70 2. Set all 12 Page State booleans from notificationPreferences mapCommon mistakes when creating a Custom Notification System with User Preferences in FlutterFlow
Why it's a problem: Updating individual preference fields with separate Firestore writes on each Switch toggle
How to avoid: Update Page State on toggle, then write the entire notificationPreferences map in a single Update Document call when the user taps Save Preferences.
Why it's a problem: Not storing the FCM token in the user document after login
How to avoid: Call FirebaseMessaging.instance.getToken() on login, store it in the user document, and listen for onTokenRefresh to keep it current.
Why it's a problem: Sending notifications without checking the user's preference map in the Cloud Function
How to avoid: Always read notificationPreferences[category][channel] in the Cloud Function before sending. Skip silently if the preference is false.
Best practices
- Store all notification preferences as a single nested map to enable atomic reads and writes
- Default orders and messages to enabled, promotions to disabled — respect user attention from the start
- Use a Save button rather than auto-saving each toggle to minimize Firestore writes
- Register and refresh the FCM token on every login to handle device changes and token rotation
- Add a Master Toggle per channel (e.g., disable all push) that sets all category push booleans at once
- Show a subtitle on each toggle explaining exactly what notifications it controls
- Log notification delivery attempts in a Firestore subcollection for debugging failed deliveries
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to build a notification preferences page in FlutterFlow where users toggle push, email, and in-app notifications for each category (orders, messages, promotions, system). Show me the Firestore user document structure, preference page widget tree, save logic, and a Cloud Function that checks preferences before sending.
Create a settings page with four sections (Orders, Messages, Promotions, System). Each section has three toggle switches labeled Push, Email, and In-App. Add a Save button at the bottom.
Frequently asked questions
How do I set default notification preferences for new users?
In the sign-up action flow, after creating the user document, write the default notificationPreferences map with orders and messages enabled, promotions disabled. This ensures new users have sensible defaults.
Can I add a master toggle to disable all push notifications at once?
Yes. Add a Switch at the top of the page labeled 'Push Notifications.' On toggle off, set all four category push booleans to false in Page State. On toggle on, restore them to true.
How does the Cloud Function know which channel to use?
The calling code passes the channel parameter (push, email, or inApp) to the sendNotification function. The function reads the corresponding boolean from the user's preferences map and only sends if it is true.
What happens if the FCM token is expired or invalid?
The Cloud Function's messaging().send() call will throw a messaging/registration-token-not-registered error. Catch this error and delete the stale fcmToken from the user document so it gets refreshed on next login.
Can I schedule notification preferences to change automatically?
Not from FlutterFlow directly. You could add a 'quiet hours' feature with startHour and endHour fields on the user document. The Cloud Function checks the current time against quiet hours before sending push notifications.
Can RapidDev help build a full notification infrastructure?
Yes. RapidDev can implement push notifications with FCM, email delivery via SendGrid, in-app notification feeds, quiet hours, user segmentation, and analytics dashboards for notification performance.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation