When I first got to integrate firebase messaging to flutter, I read the official documentation and many Medium articles and still failed to do it in the right way. There are many reasons for that: Articles I read were not beginner-friendly and also they missed few things. After going through the Github issues and few developer forums I finally understood the right way of doing it.
In this tutorial, I will try to teach you how to integrate firebase messaging (push notification) into your flutter project without getting an error. So let’s get started.
STEP 1: 💥 Create Firebase project
log in to firebase console https://console.firebase.google.com/u/0/
Click on add project, enter the name of your project and click the continue button, and follow the wizard with a couple of clicks and the project will be created
STEP 2: 📱Add Android app
First, click on the Android icon to add Android app
Now a five-step wizard will appear.Enter the package name which will be in the
android/app/build.gralde
file, look forapplicationId
in that file. Also, provide a nickname for the app (optional). Now click register.On the second step, download the
google-service.json
file and move it to[project]/android/app
location-
Add the classpath to the
[project]/android/build.gradle
file.
dependencies { // Example existing classpath classpath 'com.android.tools.build:gradle:3.5.3' // Add the google services classpath classpath 'com.google.gms:google-services:4.3.4' }
-
Add the apply plugin to the
[project]/android/app/build.gradle
file.
// ADD THIS AT THE BOTTOM apply plugin: 'com.google.gms.google-services'
-
If you want to be notified in your app (via
onResume
andonLaunch
, don't worry we will be defining these functions in the following steps) when the user clicks on a notification in the system tray include the following intent-filter within the<activity>
tag of yourandroid/app/src/main/AndroidManifest.xml
<intent-filter> <action android:name="FLUTTER_NOTIFICATION_CLICK" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>
STEP 3: 📱Add IOS app
- First, click on the IOS icon to add the IOS app
- Now a five-step wizard will appear. on the first step; Enter the bundle id. Open
ios/Runner.xcworkspace
using Xcode, you can find the bundle id there. Provide a nickname for the app for identifying in the future (optional). Now click register. - Download GoogleService-info.list and drag and drop it inside the runner folder using Xcode
- Now a window will appear, make sure you have checked everything like in the following image.
- We are good to go!. Skip all other steps.
STEP 4: ⚡ Adding packages to Flutter
Install the following dependencies in your Flutter project.
dependencies:
firebase_core: <latest_version>
firebase_messaging: <latest_version>
STEP 5: 🔥 Configuring firebase messaging
Now import the firebase messaging package and override the initState
method of the home screen widget and add the following lines.
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final _firebaseMessaging = FirebaseMessaging();
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
void initState() {
// configure firebase messaging
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
// requesting permission, only for ios
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(
sound: true, badge: true, alert: true, provisional: true));
_firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
// getting registration id
_firebaseMessaging.getToken().then((String token) async {
assert(token != null);
print('Registration Id: $token');
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
STEP 6: ✉️ Handling the received message
For example, the payload we are sending through the firebase API is:
https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=<API_KEY>
{
"notification": {
"title": "New order recieved",
"body": "2 items ordered"
},
"data": {
"status": "done",
"order_id": 2971,
"click_action": "FLUTTER_NOTIFICATION_CLICK"
},
"to": "<device id>"
}
The structure of the message we receive on our phones is different for Android and IOS. So we need to convert it into a common structure before we access it. So we will write a function that takes the message as input and convert it based on the platform the code is running on.
Map<String, dynamic> _convertMessage(Map<String, dynamic> message) {
try {
if (Platform.isIOS) {
return {
'title': message['aps']['alert']['title'],
'body': message['aps']['alert']['body'],
'order_id': message['order_id'],
'status': message['status'],
};
} else {
return {
'title': message['notification']['title'],
'body': message['notification']['body'],
'order_id': message['data']['order_id'],
'status': message['data']['status'],
};
}
} catch (e) {
return null;
}
}
}
Now we converted the message into the same structure, so it will be easy to access and do whatever we want with the received message.
That's it!. We have now successfully integrated The final code after integration will be as follows.
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final _firebaseMessaging = FirebaseMessaging();
void _incrementCounter() {
setState(() {
_counter++;
});
}
Map<String, dynamic> _convertMessage(Map<String, dynamic> message) {
try {
if (Platform.isIOS) {
return {
'title': message['aps']['alert']['title'],
'body': message['aps']['alert']['body'],
'order_id': message['order_id'],
'status': message['status'],
};
} else {
return {
'title': message['notification']['title'],
'body': message['notification']['body'],
'order_id': message['data']['order_id'],
'status': message['data']['status'],
};
}
} catch (e) {
return null;
}
}
}
@override
void initState() {
// configure firebase messaging
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
Map<String, dynamic> convertedMessage = _convertMessage(message);
if(convertedMessage != null) {
// now do your stuff here
}
},
onLaunch: (Map<String, dynamic> message) async {
Map<String, dynamic> convertedMessage = _convertMessage(message);
if(convertedMessage != null) {
// now do your stuff here
}
},
onResume: (Map<String, dynamic> message) async {
Map<String, dynamic> convertedMessage = _convertMessage(message);
if(convertedMessage != null) {
// now do your stuff here
}
},
);
// requesting permission, only for ios
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(
sound: true, badge: true, alert: true, provisional: true));
_firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
// getting registration id
_firebaseMessaging.getToken().then((String token) async {
assert(token != null);
print('Registration Id: $token');
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
📝 Things to note
By default background messaging is not enabled. Background messaging is not receiving messages while the app is running in the background, it is only used for special cases. And this feature is only available for Android. So I recommend you to not care too much about this. run tab
in Android Studio will log the following message:
E/FlutterFcmService(15395): Fatal: failed to find callback
This error message is coming from startBackgroundIsolate which is used for allowing handling background messages. If you don’t want to handle background messages then you can safely ignore this error message.
Top comments (1)
Any chance for an update on this post, given the changes to the SDK since the original writing?