Push Notifications with FlutterFlow and Supabase

Engineering
October 13, 2024
Flutterflow has direct push notificaiton integration with Firebase but only if you are using Firebase Auth. If you are using Supabase for Auth, things are much trickier. This is an complete guide to setting this up.
Sign up for our mailing list

Push notifications are quite a bit of additional work for those of us using FlutterFlow + Supabase (and Supabase Auth). The payoff is nice though–write a row to a DB table, get a notification–but we trade a more complicated setup and deployment workflow.

Shoutout to the best guides on the internet for this problem: this medium post and Supabase article. You can follow those and they will get you there but I wanted to extend these a bit for issues I ran into. As well as take it a bit further for production. I also tried to order these steps in a way that make sense without duplicating the instructions from those 2 articles.

Flow of this Article:

  1. Pre-Reqs
  2. Apple Setup
  3. Supabase and Firebase Messaging Setup
  4. FlutterFlow Changes
  5. Enabling Necessary Features for iOS and Android
  6. Testing Push Notifications
  7. Foreground Notifications
  8. Pushing and Deploying from now on

Pre-Reqs

  • An app already in the various Apple Developer consoles and access to the developer portal.
  • A Firebase Account on Blaze Plan with Firebase Cloud Messaging (FCM) enabled
  • A FlutterFlow App with Firebase enabled and setup. You don’t need to be using other Firebase features but enabling Firebase in FlutterFlow makes future steps easier. You can use the FlutterFlow guide for this.
  • Supabase CLI installed on your machine or docker image (docs)
  • This article was written in Aug 2024, versions, etc may have changed since this writing.
  • DO NOT enable Push Notifications in FlutterFlow

Apple Setup

Create a “Key” In Apple Developer Portal

This will get used by FCM to communicate with Apple. Download the key and save it somewhere safe, we’ll use it later and you won’t want to lose it just in case. I called mine “FCMPush”:

Alternatively, you can setup Certificates instead of a token. It’s a bit more work. See official docs for more info: Tokens, Certificates (If you go the Certificates route, see this Stack Overflow for generating the necessary files that you’ll need to provide FCM)

Supabase & Firebase Messaging Setup

Supabase

I recommend following the official Supabase guide here to set up the webhook and push notification.

Firebase

Within Firebase, go to project settings > Cloud Messaging and add the key from the previous step into the Apple App Configuration:

For reference, you may want to review the official Firebase docs here.

FlutterFlow Changes

Custom Action in FlutterFlow

Note, as of this writing, firebase_messaging: ^14.9.2 is the highest version you can leverage within FlutterFlow due to conflicts (versions).

1. Custom Actions - GetFCMToken()

First you’ll need an action to get the FCM Token (credit to this medium post):


import 'package:firebase_messaging/firebase_messaging.dart';

Future<String?> getFCMToken() async {
  NotificationSettings settings =
      await FirebaseMessaging.instance.getNotificationSettings();
  if (settings.authorizationStatus != AuthorizationStatus.authorized) {
    return null;
  }
  String? fcmToken = await FirebaseMessaging.instance.getToken();
  return fcmToken;
}

2. Custom Action - getPushPermission()

as well as a action to request permissions (credit):


import 'package:firebase_messaging/firebase_messaging.dart';

Future<void> getPushPermission() async {
  await FirebaseMessaging.instance.requestPermission();
}


3. Custom Notification - onTokenRefreshFCM()


import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart' show kIsWeb;

Future<void> onTokenRefreshFCM() async {
  if (!kIsWeb) {
    FirebaseMessaging.instance.onTokenRefresh.listen((String fcmToken) async {
      FFAppState().fcmTokenRefresh = true;
    });
  }
}

This custom action needs an AppState to be created, and the name needs to match the second portion after the . in this part of the action FFAppState().fcmTokenRefresh:

and the action should be added to the main.dart file as a final action:

Using the Custom Actions

You need to find the right place to request push notifications permissions and to store the FCM token. For me, this is on the Home page after logging in. The update row action stores the FCM token in the table you configured in the supabase setup. If you didn’t change anything from their guide, it would be the profiles table as the column fcm_token :

Enabling the necessary features for iOS and Android

Enabling Push Notifications and Background Modes in Xcode (iOS)

Yes you have to do this and it has to be done and stored with your source code. Here’s how to set that up:

1. Enable Github Integration within FF

  • Follow the official Guide from FlutterFlow. If this is your first time, you’ll end with a blank main branch and all the code pushed from FlutterFlow in a flutterflow branch.
  • Clone your repo to your local machine.
  • 1st time only - Rebase main off of flutterflow to get a in-sync main branch assuming you want to deploy from main from now on.
    • git rebase flutterflow
  • push main

2. Enable Capabilities in Xcode

On the main branch, open xcode and then the <PROJECT_PATH>/ios

Follow the steps in section 7 of the medium article to enable capabilities in xcode

3. Android Notification Icons

If you don’t do anything, you’ll likely get a square or circle icon on your android notifications.

  1. Open Android Studio and then the PROJECT_PATH/android directory
  2. Follow the official instructions here. At at the end you want and may need to create the folder app/res/drawable/notify_icon which holds the icon images.
  3. Configure the Icon and background if you’d like:

in AndroidManifest.xmlwithin the <application section, add lines like:


        <meta-data
          android:name="com.google.firebase.messaging.default_notification_icon"
          android:resource="@drawable/notify_icon" />
        <meta-data
          android:name="com.google.firebase.messaging.default_notification_color"
          android:resource="@color/my_purple" />


  1. I also created a app/res/values/colors.xml with colors for my app

<resources>
    <color name="my_accent">#ff2747</color>
    <color name="my_purple">#190B20</color>
</resources>


Commit and push your new main branch up to your repo. This branch now contains your latest FlutterFlow code and ios / android config changes. You could deploy from this branch via FF if you’d like but you should probable test it all first.

Testing Push Notifications

iOS

Testing an actual notification on a iphone simulator isn’t possible, but you can at least test the notification is working and opening in your app as expected. This article describes the options. I went with xcrun simctl push route and created a few test files:


{
  "Simulator Target Bundle": "BUNDLE.MY",
  "aps": {
    "alert": {
      "title": "We've negotiated a price",
      "body": "Please join the chat with the seller"
    }
  }
}

Android

You can run your app in your virtual device, and sign in to your app. Then, however you see fit (e.g writing a row in your notifications table), push a notification to yourself and it should work.

Foreground Notifications

Handling background and terminated notifications should work now but we also likely want to show a notification when the user is in the app. This challenge isn’t specific to Supabase but still useful in this post. You have a couple of options:

  1. Use a custom action with straight Flutter widget like ShowDIalog
  2. Use a component and call that component from the customAction
  3. Use a component that relies on App State to be shown, and potentially App State for the content. App State is modified via a custom action for new messages.

I opted for option 3 for now. I started with option 1 but was limited in functionality. I couldn’t get option 2 to render the way I wanted and finally landed on option 3, which is working nicely.

Example AppState:

Example Component:

Example Custom Action:


...
import 'package:firebase_messaging/firebase_messaging.dart';

Future fcmForegroundHandler(BuildContext context) async {
  FirebaseMessaging.onMessage.listen(
    (RemoteMessage message) {
      if (message.notification == null) return;

      //I pass in extra data to be used for routing and various checks not not covered in this article
      Map<String, dynamic> data = message.data ?? {"type": "default"};

      // Set AppState variables such as title and message from the FCM message
      FFAppState().update(() =>
          FFAppState().notificationTitle = message.notification?.title ?? '');
      FFAppState().update(() =>
          FFAppState().notificationMessage = message.notification?.body ?? '');
      FFAppState().update(() => 
		      FFAppState().notificationType = data!["type"]);

      // Set the AppState that I use for visibility
      FFAppState().update(() => FFAppState().newNotification = true);

      // After 8 seconds cause the component to be not be shown
      Future.delayed(Duration(seconds: 8), () {
        FFAppState().update(() => FFAppState().newNotification = false);
      });
    },
  );
}


I then embed the Component on any page I want it to be shown with a visibility conditional set to the AppState newNotification.

Pushing and Deploying from now on

Assuming this all works, you want to do an actual deployment but your workflow must change. This article has assumed you’ve not made changes in FlutterFlow but eventually you will. So these steps can be used from here on out. Rebasing is not hard but does take some understanding, and if you have to fix a conflict during the rebase or merge, it helps to have some understanding and practice of how to do it.

  1. Configure Mobile Deployment > Deployment Source, enable GitHub Repo and use the main branch (or whichever you’ve chosen)
  2. Push the latest FF changes to your repo
  3. Pull the latest - git pull
  4. Reset your local flutterflow branch off the remote (the latest from Flutterflow) - git checkout flutterflow; git reset -hard origin/flutterflow
    1. Warning this is destructive if you’ve changed this branch but you can always re-push from FF to sync up
  5. Merge flutterflow branch into your main / deployable branch - git checkout main; git merge flutterflow; git push
  6. You can bump the version directly in pubspec.yaml if you need
  7. Deploy via FlutterFlow deploy button

Sign up for our mailing list