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.
- Go to the Firebase Console.
- Click on “Add project”.
- Enter your project name, and follow the steps to configure your project settings.
- 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
- In your Firebase project, click on the Android icon to add a new Android app.
- Enter your app’s package name (e.g.,
com.example.myapp
). - Download the
google-services.json
file. - Place the
google-services.json
file in theandroid/app
directory of your Flutter project. - 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()
}
}
- 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
- In your Firebase project, click on the iOS icon to add a new iOS app.
- Enter your app’s Bundle ID (e.g.,
com.example.myapp
). - Download the
GoogleService-Info.plist
file. - In Xcode, open
ios/Runner.xcworkspace
. - Drag the
GoogleService-Info.plist
file into theRunner
directory. - Add the Firebase SDK to your
Podfile
:
pod 'Firebase/Core'
pod 'Firebase/Messaging'
- Run
pod install
in theios
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:
- Go to your Firebase project in the Firebase Console.
- Select “Cloud Messaging” from the left-hand menu.
- Click on “Send your first message”.
- Enter the notification title and body.
- Choose the target (e.g., a specific device using the FCM token).
- 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.