Souvik Biswas Mobile developer (Android, iOS, and Flutter), technical writer, IoT enthusiast, avid video game player

Flutter push notifications with Firebase Cloud Messaging

10 min read 2848

Flutter Push Notifications With Firebase Cloud Messaging

User engagement is crucial to the success of any mobile application. Push notifications help attract user attention and, when implemented properly in tandem with high-quality content, can contribute to major marketing success.

In this tutorial, we’ll demonstrate how to integrate and deliver push notifications to a Flutter application using an awesome service provided by Firebase called Firebase Cloud Messaging (FCM). It’s free and easy to get started with, and you don’t need to manage your backend infrastructure.

We’ll cover the following with detailed examples and step-by-step instructions:

What are push notifications?

If you use a smartphone, you almost certainly encounter push notifications every day. Push notifications are clickable pop-up messages that appear on your users’ devices, irrespective of whether they are using that particular app. Even when the device is idle or the user is using another app, they receive push notifications as long as the device is online and notification permissions are granted.

In this tutorial, we’ll use Firebase Cloud Messaging to send push notifications.

Firebase setup

To start using Firebase, you have to create new Firebase project.

Log in to your Google account, navigate to the Firebase console, and click Add project.

Firebase Console

Enter a project name and click Continue.

Firebase Project Name

We made a custom demo for .
No really. Click here to check it out.

Disable Google Analytics; we don’t need it for our sample project. Then, click Create project.

Disable Google Analytics in Firebase

After the project initializes, click Continue.

Firebase Project Initialized

This will take you to the Project Overview screen. Here, you’ll find options to integrate the Firebase project with your Android and iOS app.

Firebase Project Overview

Integrating Firebase with your Flutter app

Our Firebase project is now ready to be integrated with the mobile app. Though we’re using Flutter, which is a cross-platform framework, we still have to do the initial Firebase setup for both platforms separately.

First, create a new Flutter application:

flutter create notify

Next, open the Flutter project on your favorite IDE.

To open it in VS Code, you can use the following:

code notify

Android integration

To integrate your Firebase project with the Android side of the app, follow the steps below.

Click the Android icon on the project overview page:

Firebase Project Overview

This will lead to a form. First, enter the Android package name. You can find this in your project directoryandroidappsrcAndroidManifest.xml. On the second line, you’ll see your package name. Just copy and paste it into the form.

Optionally, you can choose a nickname for your app. If you leave this field empty, an auto-generated app name will be used.

Add Firebase to Android App

You have to enter the SHA-1 hash. Just hover over the help icon (?) and click See this page. This will take you to the Authenticating Your Client page:

Authenticating Your Client in Firebase

From here, you’ll get the command to generate the SHA-1 hash. Paste this into your terminal to get the SHA-1 hash, then just copy and paste the generated hash into the form.

Click Register app. This will take you to the next step.

Download the google-services.json file, drag and drop it into your project directoryandroidapp, then, click Next.

Download Google Services

Follow the instructions and add the code snippets in the desired position. Then, click Next.

Add Firebase SDK

Finally, click Continue to console.

Add Firebase to Your Android App

With this, you’ve completed the Firebase setup for the Android side of your app.

iOS integration

To integrate your Firebase project with the iOS side of your app, follow the steps outlined below.

Click the Add app present on the project overview page, then select iOS:

Firebase and Flutter FCM iOS Integration

Enter the iOS bundle ID and your App nickname. Then, click Register app.

Add Firebase to Your iOS App

You can find the bundle ID inside iosRunner.xcodeprojproject.pbxproj by searching for “PRODUCT_BUNDLE_IDENTIFIER.”

Project Bundle Identifier

Download the GoogleService-Info.plist file.

Download GoogleService-Info

Open the ios folder of the project directory in Xcode. Drag and drop the file you downloaded into the Runner subfolder. When a dialog box appears, make sure the Copy items if needed of the Destination property is checked and Runner is selected in the Add to targets box. Then, click Finish.

Firebase Flutter FCM Xcode Project Directory

You can close the Xcode now.

You can skip steps three and four; these will be handled by the Flutter Firebase plugin, which we’ll add soon. Then, click Continue to console.

Before diving into the Flutter code, you need to complete one more step in Firebase.

Go to Project settings:

Firebase Project Settings

Under the General tab, enter your Support email.

Enter Support Email in Firebase

Now the Firebase setup and integration are complete. Let’s move on to the Flutter code.

Flutter plugins

The Flutter plugins we require for this project are:

You can get these packages from pub.dev with their latest versions. Add them to the pubspec.yaml file of the Flutter project.

Building a Flutter UI

We’ll keep the UI of this Flutter app simple so we can focus on the push notification functionality.

The app will contain an AppBar and some Text widgets inside a Column to display the notification content:

Firebase Flutter Push Notifications Example App

Navigate to the lib folder from the root project directory where the Dart code is present. Replace the entire code with the following:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Notify',
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

Now we have to define the HomePage widget, which will be a StatefulWidget, because we’ll need to update the UI as soon as a notification arrives.

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _totalNotifications;

  @override
  void initState() {
    _totalNotifications = 0;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Notify'),
        brightness: Brightness.dark,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            'App for capturing Firebase Push Notifications',
            textAlign: TextAlign.center,
            style: TextStyle(
              color: Colors.black,
              fontSize: 20,
            ),
          ),
          SizedBox(height: 16.0),
          NotificationBadge(totalNotifications: _totalNotifications),
          SizedBox(height: 16.0),
          // TODO: add the notification text here
        ],
      ),
    );
  }
}

Here, we have a Scaffold containing an AppBar and a Column. The column contains a basic Text widget followed by the NotificationBadge widget for displaying the total number of notifications received. You may have noticed the TODO; this is where we’ll display the notification information.

The code for the NotificationBadge is as follows:

class NotificationBadge extends StatelessWidget {
  final int totalNotifications;

  const NotificationBadge({@required this.totalNotifications});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 40.0,
      height: 40.0,
      decoration: new BoxDecoration(
        color: Colors.red,
        shape: BoxShape.circle,
      ),
      child: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text(
            '$totalNotifications',
            style: TextStyle(color: Colors.white, fontSize: 20),
          ),
        ),
      ),
    );
  }
}

Adding push functionality with Firebase Cloud Messaging

Now it’s time to add the push functionality. To start using the FCM, create an instance of FirebaseMessaging inside the _HomePageState widget, like this:

class _HomePageState extends State<HomePage> {
  FirebaseMessaging _messaging = FirebaseMessaging();
  // ...

  @override
  Widget build(BuildContext context) {
    // ...
  }
}

Create a method called registerNotification() inside the _HomePageState class. This will initialize the Firebase app, request notification access (required only on iOS devices), and configure the messaging to receive push notifications as well as display them.

void registerNotification() async {
  // 1. Initialize the Firebase app
  await Firebase.initializeApp();

  // 2. On iOS, this helps to take the user permissions
  await _messaging.requestNotificationPermissions(
    IosNotificationSettings(
      alert: true,
      badge: true,
      provisional: false,
      sound: true,
    ),
  );

  // TODO: handle the received notifications
}

In the above code, we first initialized the Firebase app, without which we won’t be able to access any Firebase services inside the app.

The method requestNotificationPermissions() is used for taking user consent on iOS devices (if the app is run on an Android device, this is ignored).

To receive push notifications that arrive on the device, and to perform a UI change according to it, use the following code:

void registerNotification() async {
  PushNotification _notificationInfo;
  // ...

  // For handling the received notifications
  _messaging.configure(
    onMessage: (message) async {
      print('onMessage received: $message');

      // Parse the message received
      PushNotification notification = PushNotification.fromJson(message);

      setState(() {
        _notificationInfo = notification;
        _totalNotifications++;
      });
    },
  );
}

Here, the PushNotification is a model class for parsing the notification content, which will be in JSON format.

The PushNotification model class looks like this:

class PushNotification {
  PushNotification({
    this.title,
    this.body,
  });

  String title;
  String body;

  factory PushNotification.fromJson(Map<String, dynamic> json) {
    return PushNotification(
      title: json["notification"]["title"],
      body: json["notification"]["body"],
    );
  }
}

FCM generates a registration token for the client app on initial startup. It’s helpful for sending a push notification to single devices and creating device groups. You can access it by using the following code:

void registerNotification() async {
  // ...

  // Used to get the current FCM token
  _messaging.getToken().then((token) {
    print('Token: $token');
  }).catchError((e) {
    print(e);
  });
}

Reacting to a push notification

To show the notification on the UI, you can use the overlay_support plugin we added earlier. You can easily create a simple or even a custom UI effect as any notification arrives on the device.

Wrap the MaterialApp widget with the OverlaySupport widget:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return OverlaySupport(
      child: MaterialApp(
        // ...
      ),
    );
  }
}

Then you can use the showSimpleNotification() method to display the notification inside the app:

void registerNotification() async {
  _messaging.configure(
    onMessage: (message) async {
      // ...

      // For displaying the notification as an overlay
      showSimpleNotification(
        Text(_notificationInfo.title),
        leading: NotificationBadge(totalNotifications: _totalNotifications),
        subtitle: Text(_notificationInfo.body),
        background: Colors.cyan[700],
        duration: Duration(seconds: 2),
      );
    },
  );
}

Push Notifications Example App Foreground

As you might remember, we left a TODO to be completed where we have to show the notification data on the screen. You can use the _notificationInfo and _totalNotifications variables to show the information:

class _HomePageState extends State<HomePage> {
  PushNotification _notificationInfo;
  int _totalNotifications;

  // ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // ...
          _notificationInfo != null
              ? Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'TITLE: ${_notificationInfo.title}',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16.0,
                      ),
                    ),
                    SizedBox(height: 8.0),
                    Text(
                      'BODY: ${_notificationInfo.body}',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16.0,
                      ),
                    ),
                  ],
                )
              : Container(),
        ],
      ),
    );
  }
}

As you can see, we’re displaying a Column with two Text widgets to show the title and body of the notification. When the _notificationInfo is null, we just show an empty Container.

Flutter Push Notifications Example App: Empty Message Container

If you try to put the app in the background, you’ll still receive the notification. But since we’ve yet to configure how to handle background notifications, you won’t see any change in the UI as you tap on the notification to open the app.

Flutter Push Notifications Example App: No Change in the UI

Handling background notifications

To handle background notifications, we have to define a top-level function called _firebaseMessagingBackgroundHandler() and define the onBackgroundMessage property inside the _messaging.configure() method.

You can define the _firebaseMessagingBackgroundHandler() function like this:

Future<dynamic> _firebaseMessagingBackgroundHandler(
  Map<String, dynamic> message,
) async {
  // Initialize the Firebase app
  await Firebase.initializeApp();
  print('onBackgroundMessage received: $message');
}

Keep in mind that you have to define this as a top-level function, which means it should be outside of any class.

Specify the onBackgroundMessage property:

void registerNotification() async {
  // For handling the received notifications
  _messaging.configure(
    // ...
    onBackgroundMessage: _firebaseMessagingBackgroundHandler,
  );
}

If you just define this, you won’t be able to retrieve and show data within the app.

Flutter Push Notifications Example App: onBackgroundMessage

You have to define two more properties inside the configure() method:

  1. onLaunch
  2. onResume

Here, we’ll just parse the message and update the UI state:

void registerNotification() async {
  // For handling the received notifications
  _messaging.configure(
    // ...
    onLaunch: (message) async {
      print('onLaunch: $message');

      PushNotification notification = PushNotification.fromJson(message);

      setState(() {
        _notificationInfo = notification;
        _totalNotifications++;
      });
    },
    onResume: (message) async {
      print('onResume: $message');

      PushNotification notification = PushNotification.fromJson(message);

      setState(() {
        _notificationInfo = notification;
        _totalNotifications++;
      });
    },
  );
}

There is one more catch: the message JSON you receive when you tap on the notification to open the app will look like this:

onResume: {notification: {}, data: {collapse_key: com.souvikbiswas.notify, google.original_priority: high, google.sent_time: 1613101377789, google.delivered_priority: high, google.ttl: 2419200, from: 798605711100, click_action: FLUTTER_NOTIFICATION_CLICK, google.message_id: 0:1613101378029252%bc1b6e8ebc1b6e8e}}

Notice that the notification map is empty here; only data is present. So if you need to pass any data to your app, you have to send it through an additional data property. We’ll cover how to send push notifications from FCM console in a bit.

To retrieve this data, you have to make a minor modification to the model class:

class PushNotification {
  PushNotification({
    this.title,
    this.body,
    this.dataTitle,
    this.dataBody,
  });

  String title;
  String body;
  String dataTitle;
  String dataBody;

  factory PushNotification.fromJson(Map<String, dynamic> json) {
    return PushNotification(
      title: json["notification"]["title"],
      body: json["notification"]["body"],
      dataTitle: json["data"]["title"],
      dataBody: json["data"]["body"],
    );
  }
}

Here, we retrieved two additional fields from the data message.

To show them in the UI, you can make these modifications to the Text widgets where you display that information:

Text(
  'TITLE: ${_notificationInfo.title ?? _notificationInfo.dataTitle}',
  // ...
),
Text(
  'BODY: ${_notificationInfo.body ?? _notificationInfo.dataBody}',
  // ...
),

This will display the information from the data message whenever the notification message is empty.

Flutter Push Notifications Example App: Empty Message

Flutter push notifications on Android and iOS

Now that we’ve completed our example Flutter app with push notifications, it’s time to run it. To do so, we need to make some configurations to both the Android and iOS sides of the app.

Android

To run the app on your Android device, follow the steps below:

Go to androidappbuild.gradle, and enable multidex support:

android {
    defaultConfig {
        // ...
        multiDexEnabled true
    }
}

If handling background notifications, add the com.google.firebase:firebase-messaging dependency to your androidappbuild.gradle.

dependencies {
  // ...
  implementation 'com.google.firebase:firebase-messaging:<latest_version>'
}

Make sure you’re using the latest version of the dependency.

If handling background notifications, navigate to androidappsrcmain. Inside the directory with your product identifier, create a new file called Application.kt and add the following to it:

package <your_package_name>

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService


class Application : FlutterApplication(), PluginRegistrantCallback {
    override fun onCreate() {
        super.onCreate()
        FlutterFirebaseMessagingService.setPluginRegistrant(this)
    }

    override fun registerWith(registry: PluginRegistry?) {
        io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(
                registry?.registrarFor(
                        "io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
    }
}

Inside androidappsrcmainAndroidManifest.xml, add the android:name property inside the <application> tag (only required if handling background notifications) and the <intent-filter> tag inside the <activity>:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="<your_package_name>">
   <application
        <!-- Add the following line -->
        android:name=".Application"
        android:label="notify"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            <!-- ... -->
            <!-- Add this tag -->
            <intent-filter>
              <action android:name="FLUTTER_NOTIFICATION_CLICK" />
              <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <!-- ... -->
    </application>
</manifest>

The <intent-filter> tag will help to retrieve the data message while a notification arrives.

iOS

If you’re running on an iOS device, you’ll need to perform some additional setup, including enabling push notifications and background modes in Xcode.

In addition, you must have an Apple Developer account. Since Firebase Cloud Messaging integrates with the Apple Push Notification service, which only works with real devices, you’ll also need access to a physical iOS device to receive push notifications.

You can find a detailed, step-by-step guide to configuring your iOS app to receive push notifications in the official FireFlutter docs.

Sending push notifications in Flutter with Firebase Cloud Messaging

You can send notifications from the Firebase Cloud Messaging (FCM) console directly. To do so, follow the steps outlined below.

Go to the Cloud Messaging section from the left menu of the project overview page and click Send your first message.

Firebase and Flutter Cloud Messaging

Enter a notification title, text, and name, then click Next.

Firebase and Flutter FCM: Compose Notification

Set the Target to be either your Android or iOS app, or both. Click Next.

Firebase and Flutter FCM: Target

Specify the Scheduling as “Now.” Click Next.

Firebase and Flutter FCM: Scheduling a Push Notification

For a simple notification, you don’t need to provide anything in the Additional options field.

Click Review.

Firebase and Flutter FCM: Review Additional Options

Click Publish to send the notification. Don’t worry about the warning at the top; it’s only telling us we haven’t set up analytics for this project.

Firebase and Flutter FCM: Review Push Notification Message

You can send data by specifying the following in the Additional options. Here, the click_action key with the value FLUTTER_NOTIFICATION_CLICK is mandatory. Otherwise, your app won’t be able to retrieve the data message on the device.

Firebase and Flutter FCM: Review Additional Options and Publish

Click Review, then Publish to send the data notification.

Conclusion

If you’ve made it to this point, you should have a solid understanding of what push notifications are, how to integrate push notification functionality with a Flutter app, and how to send push notifications using Firebase Cloud Messaging.

There are countless other customizations you can explore when implementing push notifications in Flutter. Check out these resources to learn more:

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Souvik Biswas Mobile developer (Android, iOS, and Flutter), technical writer, IoT enthusiast, avid video game player

3 Replies to “Flutter push notifications with Firebase Cloud Messaging”

  1. That’s version 7 of FirebaseMessaging. The lastest version 9 it’s completely different. It requires to be configured with streams. Also you can’t instantiate FirebaseMessaging anymore and you can’t get tokens like that. I readed the documentation but I can’t implement it. I thought this tutorial was updated but it’s not.

    1. Have you found a way to implement yet? I hate how bad at documentation google is. It’s too mixed with web, ios, android, etc

Leave a Reply