# How to Create a Custom Notification System with User Preferences in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Free+ (Cloud Functions require Firebase Blaze plan)
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

### 2. 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.

### 3. 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.

### 4. 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.

### 5. 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 code example

File: `FlutterFlow Notification Preferences Setup`

```text
FIRESTORE DATA MODEL:
  users/{userId}
    displayName: String
    email: String
    fcmToken: String
    notificationPreferences: Map
      orders:
        push: true
        email: true
        inApp: true
      messages:
        push: true
        email: false
        inApp: true
      promotions:
        push: false
        email: false
        inApp: false
      system:
        push: true
        email: true
        inApp: true

PAGE: NotificationPreferencesPage
  Page State: ordersPush, ordersEmail, ordersInApp,
              messagesPush, messagesEmail, messagesInApp,
              promotionsPush, promotionsEmail, promotionsInApp,
              systemPush, systemEmail, systemInApp (all Boolean)

WIDGET TREE:
  Scaffold
    AppBar (title: "Notification Preferences")
    SingleChildScrollView
      Column (padding: 16)
        ├── Text "Orders" (titleMedium, bold)
        ├── _PrefRow("Push Notifications", "Order updates on your device", ordersPush)
        ├── _PrefRow("Email", "Order confirmations via email", ordersEmail)
        ├── _PrefRow("In-App", "Order updates in notification center", ordersInApp)
        ├── Divider
        ├── Text "Messages" (titleMedium, bold)
        ├── _PrefRow("Push", "New message alerts", messagesPush)
        ├── _PrefRow("Email", "Message digest emails", messagesEmail)
        ├── _PrefRow("In-App", "Messages in notification center", messagesInApp)
        ├── Divider
        ├── Text "Promotions" (titleMedium, bold)
        ├── _PrefRow("Push", "Sale and discount alerts", promotionsPush)
        ├── _PrefRow("Email", "Promotional emails", promotionsEmail)
        ├── _PrefRow("In-App", "Promo banners in app", promotionsInApp)
        ├── Divider
        ├── Text "System" (titleMedium, bold)
        ├── _PrefRow("Push", "Security and maintenance alerts", systemPush)
        ├── _PrefRow("Email", "Account security emails", systemEmail)
        ├── _PrefRow("In-App", "System notices in app", systemInApp)
        ├── SizedBox (height: 24)
        └── Button "Save Preferences" (full width, primary)
              On Tap → Update Document users/{currentUser}
                notificationPreferences: { full map from Page State }
              → Show SnackBar "Preferences saved"

_PrefRow Pattern:
  Row (padding: 12 0)
    ├── Expanded Column
    │     ├── Text (channel name, bodyLarge)
    │     └── Text (description, bodySmall, grey)
    └── Switch (bound to Page State boolean)
          On Changed → Update Page State

ON PAGE LOAD:
  1. Read current user document
  2. Set all 12 Page State booleans from notificationPreferences map
```

## Common mistakes

- **Updating individual preference fields with separate Firestore writes on each Switch toggle** — Each Switch change triggers a Firestore write. With 12 toggles, rapid toggling causes 6+ writes and potential race conditions. Fix: Update Page State on toggle, then write the entire notificationPreferences map in a single Update Document call when the user taps Save Preferences.
- **Not storing the FCM token in the user document after login** — Without the token, Cloud Functions have no way to target the user's device for push notifications. Fix: Call FirebaseMessaging.instance.getToken() on login, store it in the user document, and listen for onTokenRefresh to keep it current.
- **Sending notifications without checking the user's preference map in the Cloud Function** — Users receive notifications they explicitly disabled, leading to frustration, app uninstalls, and potential compliance issues. Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-custom-notification-system-with-user-preferences-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-custom-notification-system-with-user-preferences-in-flutterflow
