Push Notifications & Background Services

Firebase Cloud Messaging Setup

15 min Lesson 1 of 11

Firebase Cloud Messaging Setup

Firebase Cloud Messaging (FCM) is Google's free, cross-platform messaging solution that lets you reliably send notifications and data messages to Android, iOS, and web applications. In Flutter, the firebase_messaging package wraps the native FCM SDKs and exposes a unified Dart API. Before your app can receive a single push notification, you must complete three prerequisites: register the app in the Firebase Console, download the platform-specific configuration file, and add the correct Dart and native dependencies.

Note: FCM is part of the broader Firebase suite. Even if you do not use other Firebase products (Analytics, Firestore, etc.), you still need to create a Firebase project and register your app to obtain the required credentials for push notifications.

Step 1 — Create a Firebase Project

Navigate to console.firebase.google.com and sign in with a Google account. Click Add project, give it a meaningful name (e.g. my-flutter-app), optionally enable Google Analytics, and click Create project. Once provisioned, you land on the project dashboard where you can register individual platform apps.

Step 2 — Register Your Android App

On the project dashboard click the Android icon. You will be asked for the Android package name — this must match the applicationId inside android/app/build.gradle exactly (e.g. com.example.myflutterapp). After submitting, Firebase generates a google-services.json file. Download it and place it inside your Flutter project at:

android/app/google-services.json

Next, apply the Google Services Gradle plugin. Open android/build.gradle (project-level) and add the classpath dependency, then open android/app/build.gradle (app-level) and apply the plugin:

android/build.gradle (project-level)

buildscript {
    dependencies {
        // Add this line
        classpath 'com.google.gms:google-services:4.4.1'
    }
}

android/app/build.gradle (app-level) — bottom of file

// Apply the Google Services plugin LAST
apply plugin: 'com.google.gms.google-services'
Warning: The google-services.json file must sit inside android/app/, not the project root or any other folder. Placing it in the wrong directory causes a Gradle build error that can be confusing to diagnose.

Step 3 — Register Your iOS App

Back on the Firebase dashboard click the iOS icon. Enter the iOS bundle ID — this must match the value in ios/Runner.xcodeproj/project.pbxproj (e.g. com.example.myFlutterApp). Firebase generates a GoogleService-Info.plist file. Download it and add it to the project using Xcode (not just the Finder) to ensure it is included in the build target:

  1. Open ios/Runner.xcworkspace in Xcode.
  2. Right-click the Runner folder in the project navigator.
  3. Select Add Files to "Runner" and choose GoogleService-Info.plist.
  4. Ensure Copy items if needed is checked and the Runner target is selected.
Tip: iOS push notifications also require an Apple Push Notification service (APNs) key or certificate uploaded to the Firebase project under Project Settings → Cloud Messaging → Apple app configuration. Without it, FCM cannot forward messages to APNs and no notifications will arrive on a physical iOS device.

Step 4 — Add the firebase_messaging Package

With the native config files in place, install the Dart packages. Run the following commands from your Flutter project root:

# Add the FlutterFire core plugin and the messaging plugin
flutter pub add firebase_core
flutter pub add firebase_messaging

This updates pubspec.yaml and fetches the packages. Alternatively, you can add them manually to pubspec.yaml:

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^3.6.0
  firebase_messaging: ^15.1.3
Note: Always use the latest compatible versions. Check pub.dev/packages/firebase_messaging for the current stable release. The firebase_core package is mandatory — it initialises the Firebase App singleton that all other FlutterFire plugins depend on.

Step 5 — Initialise Firebase in main.dart

Firebase must be initialised before any other Firebase service is used. The entry point is main(). Because initialisation is asynchronous you must make main() an async function and await the result before calling runApp():

lib/main.dart

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

// Top-level background message handler (must be top-level, not a method)
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // Firebase must be re-initialised in the background isolate
  await Firebase.initializeApp();
  print('Background message received: ${message.messageId}');
}

Future<void> main() async {
  // Ensure Flutter binding is ready before calling native code
  WidgetsFlutterBinding.ensureInitialized();

  // Initialise Firebase
  await Firebase.initializeApp();

  // Register the background message handler
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  runApp(const MyApp());
}
Warning: The background message handler must be a top-level function (not a class method or a closure) annotated with @pragma('vm:entry-point'). Flutter runs background messages in a separate Dart isolate; instance methods and closures from the main isolate are not accessible there.

Step 6 — Request Notification Permissions (iOS & Web)

On Android 13+ and all iOS versions, you must explicitly request permission before displaying notifications. The FirebaseMessaging instance exposes a requestPermission() method:

Requesting Permission

Future<void> requestNotificationPermissions() async {
  final messaging = FirebaseMessaging.instance;

  final 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 notification permission');
  } else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
    print('User granted provisional permission');
  } else {
    print('User declined or has not accepted permission');
  }
}

Summary

To set up FCM in a Flutter project you must: (1) create a Firebase project in the console, (2) register each platform app and download the config file (google-services.json for Android, GoogleService-Info.plist for iOS), (3) apply the Google Services Gradle plugin for Android and add the plist via Xcode for iOS, (4) add firebase_core and firebase_messaging to pubspec.yaml, (5) initialise Firebase before runApp(), and (6) register a top-level background message handler. Once this scaffold is in place, your app is ready to receive and respond to push notifications.

Tip: Use the FlutterFire CLI (dart pub global activate flutterfire_cli then flutterfire configure) to automate steps 1–3. It registers the app in Firebase and writes a firebase_options.dart file containing all platform credentials — eliminating manual copy-paste errors.