Implementing Push Notifications to Engage Users and Provide Timely Information in Flutter

Push notifications are an essential feature for modern mobile applications. They allow you to engage users with timely and relevant information, even when the app is not actively running. In Flutter, implementing push notifications can significantly enhance user engagement, improve retention rates, and deliver critical updates. This article provides a comprehensive guide on how to implement push notifications in your Flutter app using Firebase Cloud Messaging (FCM).

What are Push Notifications?

Push notifications are messages that pop up on a user’s mobile device. They can be triggered by various events, such as new messages, updates, reminders, or promotional offers. They provide a direct communication channel between the app and the user, increasing user engagement and providing valuable information in a timely manner.

Why Use Push Notifications?

  • Enhanced User Engagement: Keep users informed and engaged with real-time updates.
  • Improved User Retention: Remind users to use the app and provide value that encourages them to return.
  • Timely Information Delivery: Deliver critical updates, reminders, and alerts.
  • Customized User Experience: Send personalized notifications based on user preferences and behavior.

Setting Up Firebase Cloud Messaging (FCM)

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that enables you to reliably deliver messages and notifications at no cost. FCM is the primary service used to implement push notifications in Flutter.

Step 1: Create a Firebase Project

  1. Go to the Firebase Console and sign in with your Google account.
  2. Click on “Add project” and enter a name for your project.
  3. Follow the prompts to configure your project settings.
  4. Once the project is created, select the Flutter app (iOS and Android).

Step 2: Add Firebase to Your Flutter App

  1. For Android:
    • Register your app with your project by providing your app’s package name.
    • Download the google-services.json file and add it to your android/app directory.
    • Add the Firebase SDK to your project:
      • In your project-level build.gradle file, add the Google Services plugin as a dependency:
      • dependencies {
                    classpath 'com.google.gms:google-services:4.3.10' // Check for the latest version
                  }
      • In your app-level build.gradle file, apply the Google Services plugin and add the Firebase Messaging dependency:
      • apply plugin: 'com.google.gms.google-services'
        
        dependencies {
            implementation platform('com.google.firebase:firebase-bom:30.0.0') // Check for the latest version
            implementation 'com.google.firebase:firebase-messaging-ktx'
        }
  2. For iOS:
    • Register your app with your project by providing your app’s bundle identifier.
    • Download the GoogleService-Info.plist file and add it to your Xcode project (drag and drop into the project navigator).
    • Add the Firebase SDK to your project by following the steps outlined in the Firebase console for iOS. This usually involves adding the Firebase SDK via CocoaPods or Swift Package Manager.
    • Ensure you enable Push Notifications in your Xcode project capabilities and upload your APNs certificate to Firebase.

Installing the Firebase Messaging Plugin in Flutter

Add the firebase_messaging package to your Flutter project:

dependencies:
  firebase_core: ^2.0.0
  firebase_messaging: ^14.0.0 # Check for the latest version

Run flutter pub get to install the package.

Implementing Push Notifications in Flutter

Now, let’s dive into the Flutter code to implement push notifications.

Step 1: Initialize Firebase

In your main.dart file, initialize Firebase:

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Push Notifications',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Push Notifications'),
      ),
      body: Center(
        child: Text('Push Notifications Example'),
      ),
    );
  }
}

Step 2: Get the FCM Token

To receive push notifications, you need to obtain the FCM token for the device. This token is used to send notifications to specific devices.

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
  @override
  void initState() {
    super.initState();
    getToken();
  }

  Future getToken() async {
    String? token = await FirebaseMessaging.instance.getToken();
    print('FCM Token: $token');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Push Notifications'),
      ),
      body: Center(
        child: Text('Push Notifications Example'),
      ),
    );
  }
}

Step 3: Request Notification Permissions

Before you can receive push notifications on iOS and some versions of Android, you need to request permission from the user:

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
  @override
  void initState() {
    super.initState();
    requestPermissions();
    getToken();
  }

  Future requestPermissions() async {
    FirebaseMessaging messaging = FirebaseMessaging.instance;

    NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      sound: true,
    );

    print('User granted permission: ${settings.authorizationStatus}');
  }

  Future getToken() async {
    String? token = await FirebaseMessaging.instance.getToken();
    print('FCM Token: $token');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Push Notifications'),
      ),
      body: Center(
        child: Text('Push Notifications Example'),
      ),
    );
  }
}

Step 4: Handle Incoming Notifications

To handle incoming notifications, you need to listen for different types of messages:

  • Foreground Notifications: Notifications received when the app is open.
  • Background Notifications: Notifications received when the app is in the background or terminated.
Handling Foreground Notifications
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
  @override
  void initState() {
    super.initState();
    requestPermissions();
    getToken();
    listenForForegroundNotifications();
  }

  Future requestPermissions() async {
    FirebaseMessaging messaging = FirebaseMessaging.instance;

    NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      sound: true,
    );

    print('User granted permission: ${settings.authorizationStatus}');
  }

  Future getToken() async {
    String? token = await FirebaseMessaging.instance.getToken();
    print('FCM Token: $token');
  }

  void listenForForegroundNotifications() {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      print('Got a message whilst in the foreground!');
      print('Message data: ${message.data}');

      if (message.notification != null) {
        print('Message also contained a notification: ${message.notification}');
        // Display the notification using a dialog or a local notification plugin
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: Text(message.notification?.title ?? 'Notification'),
              content: Text(message.notification?.body ?? 'No message body'),
              actions: [
                TextButton(
                  child: Text('OK'),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
              ],
            );
          },
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Push Notifications'),
      ),
      body: Center(
        child: Text('Push Notifications Example'),
      ),
    );
  }
}
Handling Background Notifications

To handle notifications when the app is in the background or terminated, use the FirebaseMessaging.onBackgroundMessage handler.

First, define a top-level function (outside any class) to handle the background message:

import 'package:firebase_messaging/firebase_messaging.dart';

Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp();

  print("Handling a background message: ${message.messageId}");
  // Handle the notification here
}

Then, set this handler in your main function:

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';

Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print("Handling a background message: ${message.messageId}");
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Set the background messaging handler early on, as a named top-level function
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  runApp(MyApp());
}

Testing Push Notifications

You can test push notifications by sending messages from the Firebase Console:

  1. Go to the Firebase Console.
  2. Select your project.
  3. Go to “Cloud Messaging”.
  4. Click on “Send your first message”.
  5. Enter the notification title and text.
  6. Select the target (either a specific device via FCM token or all users).
  7. Send the message.

You should receive the notification on your device.

Conclusion

Implementing push notifications in Flutter using Firebase Cloud Messaging (FCM) is a powerful way to engage users and provide timely information. By following the steps outlined in this article, you can set up FCM, request notification permissions, handle foreground and background notifications, and test your implementation. Push notifications are an essential tool for modern mobile applications, and mastering their implementation in Flutter can significantly enhance your app’s user experience and engagement.