Implementing Push Notifications in Flutter

Push notifications are an essential feature for modern mobile applications, providing a way to engage users and deliver timely information. Flutter, Google’s UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase, makes implementing push notifications relatively straightforward. This blog post provides a comprehensive guide to implementing push notifications in Flutter using Firebase Cloud Messaging (FCM).

What are Push Notifications?

Push notifications are messages that appear on a user’s device outside of the application interface. They are used to deliver updates, promotions, reminders, or any information that requires the user’s immediate attention. These notifications are sent from a server to the user’s device, which is typically managed by a platform-specific push notification service (e.g., Firebase Cloud Messaging for Android and iOS).

Why Implement Push Notifications?

  • User Engagement: Keep users informed and engaged with your app.
  • Real-time Updates: Deliver instant updates on events, news, or messages.
  • Promotional Opportunities: Send promotions and offers to encourage app usage.
  • Improved User Experience: Provide timely and relevant information, enhancing the overall app experience.

How to Implement Push Notifications in Flutter

To implement push notifications in Flutter, we’ll primarily use Firebase Cloud Messaging (FCM). This guide covers setting up FCM, configuring your Flutter project, and handling incoming notifications.

Step 1: Set up Firebase Project

First, create a Firebase project if you don’t already have one.

  1. Go to the Firebase Console.
  2. Click on “Add project”.
  3. Enter your project name, and follow the steps to configure your project settings.
  4. Once the project is created, click on your project to access the Firebase dashboard.

Step 2: Add Firebase to Your Flutter App

Next, add Firebase to both your Android and iOS Flutter apps.

Add Firebase to Android
  1. In your Firebase project, click on the Android icon to add a new Android app.
  2. Enter your app’s package name (e.g., com.example.myapp).
  3. Download the google-services.json file.
  4. Place the google-services.json file in the android/app directory of your Flutter project.
  5. Modify your android/build.gradle file to include the following:

// Top-level build.gradle
buildscript {
  dependencies {
    classpath 'com.google.gms:google-services:4.3.10' // or newer version
  }
}

// Allprojects block
allprojects {
  repositories {
    google()
    jcenter()
  }
}
  1. Modify your android/app/build.gradle file to include the following:

// Apply the Google Services Gradle plugin
apply plugin: 'com.google.gms.google-services'

dependencies {
  implementation platform('com.google.firebase:firebase-bom:32.8.0') // or newer
  implementation 'com.google.firebase:firebase-messaging-ktx' // Add FCM dependency
}
Add Firebase to iOS
  1. In your Firebase project, click on the iOS icon to add a new iOS app.
  2. Enter your app’s Bundle ID (e.g., com.example.myapp).
  3. Download the GoogleService-Info.plist file.
  4. In Xcode, open ios/Runner.xcworkspace.
  5. Drag the GoogleService-Info.plist file into the Runner directory.
  6. Add the Firebase SDK to your Podfile:

pod 'Firebase/Core'
pod 'Firebase/Messaging'
  1. Run pod install in the ios directory.

Step 3: Install the firebase_messaging Plugin

Add the firebase_messaging plugin to your Flutter project.


dependencies:
  firebase_core: ^2.25.3
  firebase_messaging: ^14.6.0

Run flutter pub get to install the dependencies.

Step 4: Initialize Firebase in Your Flutter App

Initialize Firebase in your Flutter app’s main() function.


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: Scaffold(
        appBar: AppBar(
          title: Text('Push Notifications Demo'),
        ),
        body: Center(
          child: Text('Push Notifications Example'),
        ),
      ),
    );
  }
}

Step 5: Request Notification Permissions

Request notification permissions from the user in your Flutter app.


import 'package:firebase_messaging/firebase_messaging.dart';

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

  void 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,
    );

    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      print('User granted permission');
    } else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
      print('User granted provisional permission');
    } else {
      print('User declined or has not accepted permission');
    }
  }

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

Step 6: Get the Device Token

Get the FCM token for the device. This token is required to send push notifications to the specific device.


import 'package:firebase_messaging/firebase_messaging.dart';

class _MyAppState extends State {
  String? token;

  @override
  void initState() {
    super.initState();
    requestPermissions();
    getToken();
  }

  void getToken() async {
    token = await FirebaseMessaging.instance.getToken();
    print('Token: $token');
  }

  void requestPermissions() async {
    // Same as previous example
  }

  @override
  Widget build(BuildContext context) {
    // Same as previous example
  }
}

Step 7: Handle Incoming Messages

Handle incoming push notifications. There are different scenarios to consider:

  • App in Foreground: Use FirebaseMessaging.onMessage.
  • App in Background: Use FirebaseMessaging.onMessageOpenedApp.
  • App Terminated: Use FirebaseMessaging.onBackgroundMessage.

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

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

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

  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation()
      ?.createNotificationChannel(channel);

  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State {
  String? token;

  @override
  void initState() {
    super.initState();
    requestPermissions();
    getToken();
    listenForMessages();
  }

  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();

  final AndroidNotificationChannel channel = const AndroidNotificationChannel(
    'high_importance_channel', // id
    'High Importance Notifications', // title
    description:
        'This channel is used for important notifications.', // description
    importance: Importance.max,
    playSound: true,
  );

  void listenForMessages() {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification? notification = message.notification;
      AndroidNotification? android = message.notification?.android;

      if (notification != null && android != null) {
        flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              channelDescription: channel.description,
              color: Colors.blue,
              playSound: true,
              icon: '@mipmap/ic_launcher',
            ),
          ),
        );
      }
    });

    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      print('Message clicked!');
    });
  }

  void getToken() async {
    token = await FirebaseMessaging.instance.getToken();
    print('Token: $token');
  }

  void requestPermissions() async {
    // Same as previous example
  }

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

Explanation:

  • _firebaseMessagingBackgroundHandler: Handles messages when the app is in the background or terminated.
  • listenForMessages: Listens for incoming messages when the app is in the foreground.
  • flutterLocalNotificationsPlugin: Used to show a local notification when the app is in the foreground.

Step 8: Send a Test Notification

You can send test notifications from the Firebase console:

  1. Go to your Firebase project in the Firebase Console.
  2. Select “Cloud Messaging” from the left-hand menu.
  3. Click on “Send your first message”.
  4. Enter the notification title and body.
  5. Choose the target (e.g., a specific device using the FCM token).
  6. Send the test notification.

Conclusion

Implementing push notifications in Flutter using Firebase Cloud Messaging (FCM) enhances user engagement and delivers timely information. By following these steps, you can set up FCM, configure your Flutter project, and handle incoming notifications effectively. Push notifications improve the user experience and provide a powerful means of communication between your app and its users.