You’ve probably heard a lot about the importance of engagement. But re-engagement? Not so much. However, a rock-solid re-engagement strategy is essential if you want to stand out in a busy Android ecosystem.
This is where push notifications come in. Specifically, Firebase Cloud Messaging (FCM). FCM is the go-to integrated push notification solution for Android. And it’s no wonder why: FCM massively simplifies the cross-platform push notification implementation process.
Even better: FCM can help make your notifications consistent. Consistent across channels, consistent across Android devices, consistent and reliable experience for the user.
This guide will go over everything you need to know to get your React Native or Flutter push notifications for Android off the ground. We’ll explain the full FCM setup, Android Notification Channels, and even handling messages in the cross-platform layer.
The foundation: Firebase Cloud Messaging (FCM)
Studies show that around half of all Android apps rely on some form of push notifications. And most of those use Firebase Cloud Messaging for Android. Just think of the alternative! Does your startup have the time or resources to build its very own seamless messaging system? Probably not.
So, let’s take a closer look at FCM, what it is and how to set it up:
Why FCM is the Android backbone
FCM isn’t the only push notification API. Courier springs to mind, as well as Apple Push Notification service (APNs) for iOS. But FCM is by far the most popular for Android apps.
There are a few good reasons why:
- It’s free: developers can send unlimited push notifications without paying a cent. Perfect for small businesses on a budget!
- It’s native: FCM is built and maintained by Google, the same company that makes Android. So it’s already tightly integrated and smooth to run.
- It’s reliable: FCM can handle millions of messages quickly and securely. It even works when devices are asleep or offline.
Required project setup
Let’s break down the mandatory native steps when setting up Firebase Cloud Messaging for Android:
Step 1: Create a Firebase project & register your app
First things first, you’re going to have to register your app. Here’s how to do it:
- Head over to the Firebase Console.
- Click “Add project”. Give your project a name and then follow the setup wizard.
- Click “Add app” and choose the Android icon.
- Enter your app’s package name. This is your project’s unique identifier. For example, “com.example.myapp”. Note: This name should match exactly the one in your app’s AndroidManifest.xml.
- Click “Register app”.
Step 2: Download & add the google-services.json file
Your app should now be registered and you should be ready to move onto the next step. It’s time to unlock the FCM configuration file - this’ll tell your app how to interact with Firebase.
You should now have a file named google-services.json. This file contains that key configuration info (like API keys).
1.Download the JSON file from the Firebase Console.
2.Place it in your Android project like so:
app/
google-services.json3.You now have to add a few lines in your Gradle files (project-level and app-level).
Project-level build.gradle:
dependencies {
classpath 'com.google.gms:google-services:4.4.2' // example version
}
App-level build.gradle:
apply plugin: 'com.google.gms.google-services'Step 3: Understand FCM registration tokens
Registration tokens are key to the whole push notification operation. Your app won’t be able to individualize Flutter or React Native push notifications in Android.
That’s because FCM registration tokens are basically “mailing addresses” for notifications. Firebase generates a unique registration token for every new user when they first open the app. You’ve probably got dozens, even if you don’t know it!
But there’s a catch: your own server needs to know a device’s token in order to actually send them push notifications.
So, your app should send the token to your back-end system immediately. Then, when it comes time to send out a message, your app can simply say “send this message to token X” and Firebase will take care of the rest!
The Android-Specific Requirement: Notification Channels
Both users and developers have to adapt with the rise of Android Notification Channels.
From Android 8.0 onwards, users will be able to fully control their push notification preferences. Gone are the days of choosing between “on” and “off”. Now, users can fully customize their notifications.
They might choose to keep order updates on but mute promotions, for example. Or show receipts silently.
But it does mean one thing: Android app developers now have to create specific channels (categories) for this to work. You’ll need to create separate channels for:
- Order updates
- Promotions
- Receipts
And indeed all types of push notification. Luckily, this is fairly straightforward. Just create a channel in your app’s code like so:
val channel = NotificationChannel(
"order_updates",
"Order Updates",
NotificationManager.IMPORTANCE_HIGH
)Then, when you send a notification, just show which channel it belongs to:
builder.setChannelId("order_updates")
Business value of channels
Android Notification Channels may seem like extra work. But in reality, they’re just as useful for you as for your users.
Businesses can view Notification Channels as a retention feature. Before Android 8.0, users may have felt they had to keep push notifications on for a specific app so as not to miss important notifications, but were annoyed by constant bombardments. This, in turn, often led them to simply uninstall the app.
Apps are now better equipped to offer real value and keep users connected.
Implementation logic
Notification Channels are part of the Android OS, not the cross-platform layer. This means you must create them in the Android code itself and not in Firebase or the back-end.
Create Notification Channels in the Android native setup layer inside the Android module if you’re using a cross-platform framework like Flutter or React Native.
The good news is you only have to do this once. You would usually create the notification channels you need when the app launches for the first time.
Cross-platform push notification implementation: bridging the gap
Android systems handle notifications natively. But cross-platform frameworks run your app’s logic in a shared layer. So, to make a super-smooth FCM that works every time, you’ll need to “bridge the gap” and help them work together.
This is where bridging libraries come in. These basically act as the “translator” between your app’s cross-platform code and the native FCM SDKs. The go-to libraries are:
- React Native - @react-native-firebase/messaging
- Flutter - firebase_messaging
Token and permission handling
Bridging libraries help with another thing, too: fetching FCM registration tokens. No need for native code!
Here’s an example for React Native:
import messaging from '@react-native-firebase/messaging';
const token = await messaging().getToken();
console.log('FCM Token:', token);And Flutter:
import 'package:firebase_messaging/firebase_messaging.dart';
final fcmToken = await FirebaseMessaging.instance.getToken();
print('FCM Token: $fcmToken');However, a note on permissions: since Android 13, apps must request user consent before showing notifications. Again, though, bridging libraries simplify this.
React Native:
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;Flutter:
NotificationSettings settings =
await FirebaseMessaging.instance.requestPermission();Foreground vs. background message handling
How the notification arrives is another consideration. It all depends if the app is in the foreground or background, or terminated.
For foreground messages (i.e., when the app is open), the app has to decide how to push the notification. It doesn’t necessarily show up on a banner. You can either:
- Display a custom in-app banner.
- Log the data silently.
- Trigger in-app UI updates.
React Native example:
messaging().onMessage(async remoteMessage => {
console.log('Foreground message:', remoteMessage);
// Optionally show a custom alert or local notification.
});Flutter example:
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Received message while in foreground: ${message.notification?.title}');
// Optionally show a custom notification or snackbar here.
});Things are different when the app is in the background or terminated. FCM automatically displays notification messages. But exactly how depends on your framework.
React Native uses a headless JavaScript task:
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in background:', remoteMessage);
});Flutter uses a background Dart isolate:
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
print('Handling a background message: ${message.messageId}');
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);Back-end strategy: sending the payload to Android
As your server has to send a structured payload to Firebase’s FCM endpoint, which it then delivers to the registration token, your back end is vital for delivering notifications effectively.
Here are some steps you need to take to up your back-end game:
Optimizing the message payload
There are two types of payloads: notification messages and data messages. Each is suited to a different use:
- Notification messages: displayed automatically by FCM and the Android system when the app is in the background or killed. FCM handles the display. These are ideal for simple alerts and updates.
- Data messages: contain only a data object. Your app must handle them in code as they’re not displayed automatically. Generally flexible and can trigger in-app behavior. Great for silent updates and deep linking.
Priority and delivery
Annoying users with “urgent” alerts that turn out to be sales promotions is a sure way to turn people off. So, it’s a good idea to make use of FCM’s two message priority levels: “high” and “normal”.
- High priority - these actually wake the device (draining battery), so save them for truly urgent alerts like alarms or messages.
- Normal priority - these come through when the device is next active. Better suited for most notification types (like non-urgent updates or summaries).
Targeting and deep linking in Android notifications
Your goal is to make an app experience that guides users exactly where they need to go with minimal friction. With that in mind, you need to think about what happens when the user actually taps a notification. For this, you need custom data fields in the payload.
Here’s an example:
{
"to": "DEVICE_TOKEN",
"notification": {
"title": "Your Order Has Arrived!",
"body": "Tap to view your delivery details"
},
"data": {
"screen": "OrderDetails",
"order_id": "98765"
}
}The app will navigate the user directly to that specific screen. This is what we call “deep linking” in Android notifications and it’s crucial for seamless UX.
Best practices for Android ecosystem success
There’s more to push notifications than correct coding. There are a few other steps you should take to make sure you’re delivering a fulfilling notification experience that delivers on the user’s needs.
We’ve condensed our experience into a few push notification best practices you should follow:
Icon and customization
Visuals matter. You want your notifications to be both clear and on-brand. After all, this could be the first interaction a new user has with your business.
Here are some tips:
- Use a dedicated notification icon and avoid using your launcher icon (it’s often too detailed).
- Set a consistent accent color that reflects your brand and highlights the icon background in the system tray.
- Customize notification sound that reinforces your brand identity every time it rings. Just try not to make it annoying!
Avoiding doze mode
Android’s battery-saving doze mode is great for users. But it’s not so good for non-urgent notifications. Normal-priority FCM messages can easily be pushed down the priority list.
The way around this is, of course, High Priority FCM messaging. These will always wake the device, even in doze mode. However, you should be careful not to overuse High Priority. Android could throttle it if abused!
Testing across manufacturers
The reality is different Android devices have different battery optimization settings that can affect notification behavior. Samsung, Xiaomi, Huawei, and others all do things their own way. So, you need to make sure your notifications work across the ecosystem.
- Test - you should test your notifications on major brands like:
- Google Pixel
- Samsung
- Xiaomi / Redmi
- Vivo
- OnePlus
- Check specific restrictions - some devices automatically delay notifications unless the app is whitelisted, for example. Make sure you’re aware of this.
- Verify behavior in different app states - double-check everything works smoothly whether the app is in the foreground, background, killed, or rebooted across devices.
Next steps
Perfect Android push notifications don’t happen overnight. It can take many months of careful Notification Channels categorization and cross-platform bridging to get it just right. But the rewards are worth it: seamless push notifications are one of the key retention weapons you have in your arsenal. Make it count.
You don’t have to do it alone. Tapptitude experts can help you with everything from architectural design to complex cross-platform implementation. Reach out to our team and discover how you can get to market quicker with Tapptitude!
Tapptitude
Tapptitude is a mobile app development company specialized in providing high-quality mobile app development services, and a top-rated app company on Clutch.