# How to Send Push Notifications to Groups of Users in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 30-45 min
- Compatibility: FlutterFlow Free+ (FCM topics); Cloud Functions requires Blaze plan
- Last updated: March 2026

## TL;DR

To send push notifications to groups in FlutterFlow, use Firebase Cloud Messaging topics for broad opt-in groups, or query a Firestore user segment and call sendEachForMulticast via a Cloud Function. Topics are best for interests or regions; Firestore queries are best for behavior-based segments like 'users who haven't opened the app in 7 days.'

## Two Patterns for Group Notifications

FlutterFlow's built-in push notification action sends to a single FCM token — one user at a time. For groups you need two additional techniques. FCM topics let a device subscribe to a named channel (e.g., 'promotions' or 'region-us-east'). When you publish to that topic, Firebase fans the message out to every subscriber automatically — no loop required. The second pattern uses a Cloud Function that queries Firestore for a matching segment (e.g., all users where plan == 'pro'), collects their FCM tokens, and calls the Admin SDK's sendEachForMulticast. Both patterns are triggered from FlutterFlow via a custom API call or Cloud Function action.

## Before you start

- Firebase project connected to FlutterFlow with push notifications enabled
- FCM server key added to FlutterFlow (Settings > Push Notifications)
- Firebase Blaze plan if using Cloud Functions
- Users collection in Firestore with an fcmToken field updated on login

## Step-by-step guide

### 1. Store the FCM token on every login

Before you can target groups you need every user's current FCM token saved in Firestore. In FlutterFlow open your app's initial action flow (typically triggered on the splash or home page load). Add a Custom Action that calls FirebaseMessaging.instance.getToken() and writes the result into the current user's Firestore document under the field fcmToken. Also store a lastSeen timestamp and any segment fields such as plan, region, or interests as an array. This document becomes the source of truth for all group targeting.

```
// Custom Action: updateFcmToken.dart
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

Future updateFcmToken() async {
  final token = await FirebaseMessaging.instance.getToken();
  final uid = FirebaseAuth.instance.currentUser?.uid;
  if (uid != null && token != null) {
    await FirebaseFirestore.instance.collection('users').doc(uid).update({
      'fcmToken': token,
      'lastSeen': FieldValue.serverTimestamp(),
    });
  }
}
```

**Expected result:** Every user document in Firestore has a populated fcmToken field after login.

### 2. Subscribe users to FCM topics based on preferences

FCM topics are the simplest broadcast mechanism. A device subscribes to a named string topic, and any message sent to that topic is delivered to all subscribers. In FlutterFlow create a Custom Action called subscribeToTopic. Call FirebaseMessaging.instance.subscribeToTopic(topicName). Trigger this action from your onboarding flow when users select interests (sports, deals, news) or when they opt into a role-based group (admins, beta-testers). Add matching unsubscribeFromTopic actions on your notification preferences screen so users can opt out without you needing to maintain a token list.

```
// Custom Action: subscribeToTopic.dart
import 'package:firebase_messaging/firebase_messaging.dart';

Future subscribeToTopic(String topicName) async {
  await FirebaseMessaging.instance.subscribeToTopic(topicName);
}

Future unsubscribeFromTopic(String topicName) async {
  await FirebaseMessaging.instance.unsubscribeFromTopic(topicName);
}
```

**Expected result:** Users are enrolled in one or more FCM topics. You can verify in Firebase Console > Cloud Messaging > Topics.

### 3. Build a notification preferences screen with per-topic toggles

Give users control by building a Preferences page in FlutterFlow. Add a Column with one Row per topic: a Text label ('Promotions', 'New Features', 'Regional Updates') and a Toggle widget. Bind each Toggle's initial value to a boolean field on the user's Firestore document (e.g., notif_promotions). On the Toggle's On Changed action, first call the subscribeToTopic or unsubscribeFromTopic Custom Action depending on the new value, then update the matching Firestore field. This approach keeps server state and on-device subscriptions in sync, so you can also filter users in Cloud Functions using the Firestore field.

**Expected result:** Toggling a preference immediately subscribes or unsubscribes the device and updates Firestore.

### 4. Send to a topic via a Cloud Function

Create a Firebase Cloud Function (Node.js) that accepts a topic name and message payload and uses the Admin SDK to publish. Deploy it and call it from FlutterFlow using a Custom API Call or the Cloud Function action. In FlutterFlow go to Settings > Cloud Functions, add your deployed function, map the topic, title, and body parameters, and trigger it from an admin screen or a backend scheduled task. Only admin users should be able to trigger this — add a Firebase security rule or check custom claims inside the function.

```
// functions/index.js
const { onCall } = require('firebase-functions/v2/https');
const { getMessaging } = require('firebase-admin/messaging');
const { initializeApp } = require('firebase-admin/app');

initializeApp();

exports.sendToTopic = onCall(async (request) => {
  const { topic, title, body, data } = request.data;
  if (!request.auth?.token?.admin) {
    throw new Error('Unauthorized');
  }
  const message = {
    notification: { title, body },
    data: data || {},
    topic,
  };
  const response = await getMessaging().send(message);
  return { messageId: response };
});
```

**Expected result:** Calling the function delivers a push notification to every device subscribed to that topic within seconds to minutes.

### 5. Send to a Firestore-queried segment with sendEachForMulticast

For dynamic segments not suitable for topics (e.g., 'users who purchased in the last 30 days'), use a Cloud Function that queries Firestore, collects up to 500 FCM tokens per batch, and calls sendEachForMulticast. Create a scheduled Cloud Function or an onCall function with an admin guard. Query your users collection with the desired filter, map results to their fcmToken fields, chunk into arrays of 500, and send. Log failed tokens and remove them from Firestore to keep your token list clean.

```
// functions/sendToSegment.js
const { onCall } = require('firebase-functions/v2/https');
const { getMessaging } = require('firebase-admin/messaging');
const { getFirestore } = require('firebase-admin/firestore');

exports.sendToSegment = onCall(async (request) => {
  const { filter, title, body } = request.data;
  const db = getFirestore();
  let query = db.collection('users');
  if (filter.plan) query = query.where('plan', '==', filter.plan);
  if (filter.region) query = query.where('region', '==', filter.region);
  const snap = await query.get();
  const tokens = snap.docs
    .map(d => d.data().fcmToken)
    .filter(Boolean);
  const chunkSize = 500;
  let sent = 0;
  for (let i = 0; i < tokens.length; i += chunkSize) {
    const chunk = tokens.slice(i, i + chunkSize);
    const result = await getMessaging().sendEachForMulticast({
      tokens: chunk,
      notification: { title, body },
    });
    sent += result.successCount;
  }
  return { sent };
});
```

**Expected result:** The function returns the number of successfully delivered messages and the segment receives the notification.

## Complete code example

File: `functions/index.js`

```javascript
const { onCall, onSchedule } = require('firebase-functions/v2/https');
const { getMessaging } = require('firebase-admin/messaging');
const { getFirestore } = require('firebase-admin/firestore');
const { initializeApp } = require('firebase-admin/app');

initializeApp();

// Send to FCM topic (admin only)
exports.sendToTopic = onCall(async (request) => {
  if (!request.auth?.token?.admin) throw new Error('Unauthorized');
  const { topic, title, body, data } = request.data;
  const response = await getMessaging().send({
    notification: { title, body },
    data: data || {},
    topic,
  });
  return { messageId: response };
});

// Send to Firestore segment (admin only)
exports.sendToSegment = onCall(async (request) => {
  if (!request.auth?.token?.admin) throw new Error('Unauthorized');
  const { filter, title, body } = request.data;
  const db = getFirestore();
  let query = db.collection('users');
  if (filter?.plan) query = query.where('plan', '==', filter.plan);
  if (filter?.region) query = query.where('region', '==', filter.region);
  const snap = await query.get();
  const tokens = snap.docs.map(d => d.data().fcmToken).filter(Boolean);
  let sent = 0;
  const staleTokens = [];
  for (let i = 0; i < tokens.length; i += 500) {
    const chunk = tokens.slice(i, i + 500);
    const result = await getMessaging().sendEachForMulticast({
      tokens: chunk,
      notification: { title, body },
    });
    sent += result.successCount;
    result.responses.forEach((r, idx) => {
      if (!r.success && r.error?.code === 'messaging/registration-token-not-registered') {
        staleTokens.push(chunk[idx]);
      }
    });
  }
  // Clean up stale tokens
  if (staleTokens.length > 0) {
    const batch = db.batch();
    const staleSnap = await db.collection('users')
      .where('fcmToken', 'in', staleTokens.slice(0, 10)).get();
    staleSnap.docs.forEach(d => batch.update(d.ref, { fcmToken: null }));
    await batch.commit();
  }
  return { sent, staleRemoved: staleTokens.length };
});
```

## Common mistakes

- **Subscribing all users to all topics by default** — Users receive notifications for topics they never opted into, leading to immediate opt-outs and app uninstalls. Fix: Subscribe users only when they explicitly select an interest during onboarding or from a preferences screen. Default all topic subscriptions to off.
- **Sending FCM tokens directly from the FlutterFlow client to another user's device** — FCM server keys must never be exposed on the client — anyone who obtains the key can send arbitrary notifications to your entire user base. Fix: Always send group notifications from a Cloud Function or a secure backend that holds the FCM server key. The client only triggers the function.
- **Not chunking tokens into batches of 500 for sendEachForMulticast** — The FCM Admin SDK throws an error if you pass more than 500 tokens in a single multicast call. Fix: Slice the token array into chunks of 500 and loop, accumulating success counts as shown in the complete code.
- **Ignoring stale token cleanup after delivery** — Uninstalled apps or tokens that have been refreshed return UNREGISTERED errors. Accumulating stale tokens wastes quota and inflates segment size. Fix: Check each response in sendEachForMulticast results and delete or null out any token that returns messaging/registration-token-not-registered.

## Best practices

- Always use topics for static interest groups (sports, promotions) and Firestore queries for dynamic behavioral segments.
- Provide a notification preferences screen so users can opt out of individual topics without uninstalling.
- Rate-limit your admin send screen to prevent accidental duplicate broadcasts to large groups.
- Log every group send in a Firestore notifications_log collection with timestamp, topic, segment, and sent count for audit purposes.
- Test with Firebase's notification composer in the Firebase Console before building your admin UI — it lets you send to topics without any code.
- Use custom data payloads in addition to notification payloads so the app can route users to the correct screen on tap.
- Monitor FCM delivery rates in Firebase Console > Cloud Messaging > Analytics and set up alerts for sudden drops.

## Frequently asked questions

### How many users can I send to with FCM topics?

There is no documented hard limit on topic subscribers. FCM has handled topics with tens of millions of subscribers. Very large topics (millions) may take a few minutes to fully fan out.

### Do FCM topic subscriptions persist if the user uninstalls and reinstalls the app?

No. Uninstalling clears the FCM token and all topic subscriptions. When the user reinstalls, your onboarding flow must re-subscribe them to their saved topics, which is why storing preferences in Firestore is important.

### What is the difference between FCM topics and multicast?

Topics are managed on the FCM server — devices opt in and the server handles delivery. Multicast requires you to maintain and pass a list of up to 500 tokens per call. Topics are simpler for static groups; multicast is better for dynamic, query-based segments.

### Can I send group notifications without the Firebase Blaze plan?

You can subscribe devices to FCM topics on the free Spark plan and send topic messages using the Firebase Console's notification composer. You only need the Blaze plan if you use Cloud Functions to send programmatically.

### How do I create location-based notification groups?

Subscribe users to a region topic (e.g., 'region_us_west') based on their stored location in Firestore, or use geohash queries in a Cloud Function to build a token list. Update the subscription whenever the user changes their location preference.

### What happens if I call sendEachForMulticast with an expired token?

The response for that token will have success: false and an error code of messaging/registration-token-not-registered. You should detect this and remove the stale token from Firestore to keep your token list accurate.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-can-i-send-push-notifications-to-groups-of-users
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-can-i-send-push-notifications-to-groups-of-users
