Phase 4: Advanced Mobile

Foreground, background & killed-state notification handling

Intermediate ~3 min read
Think of it this way A friendly analogy. Read this if the technical version feels dense. Show Hide

Imagine your phone app is like a busy kitchen, and you are the head chef! All sorts of delicious things are being prepared, and your goal is to make sure everything runs smoothly for anyone using your app. Now, sometimes, an important message, like a "push notification," needs to get your attention. How your app handles that message depends on whether your kitchen is bustling, or if you've stepped away for a bit.

First, let's talk about the foreground state. This is when your app is wide open on someone’s phone, and they're actively using it, just like you’re right there in the kitchen, chopping vegetables and stirring pots. If a message arrives now – maybe it's the oven timer beeping for perfectly baked cookies – you, the chef, hear it directly. You don't need a huge, loud fire alarm going off to tell you. Instead, you calmly glance at the oven, take out the cookies, and perhaps update your mental checklist, all without stopping your main cooking flow. Your app gets to decide exactly how to show that message, maybe by subtly updating a score in a game or showing a little "new message" bubble inside the app, without interrupting what the user is doing with a big system pop-up.

Next is the background state. This is like when you’ve stepped out of the kitchen for a moment, maybe to watch a quick cartoon in the living room. Your app is still running, perhaps doing some work in the background, but it's not visible on the screen. If something important happens now – like the cookies burning and the smoke detector (which is like your phone’s built-in alert system) blaring throughout the house – it gets your attention! The phone’s operating system takes charge, showing a banner or a sound to let you know, because your app isn’t front and center to handle it quietly. Even if you've completely closed your app and gone to the park (that's the "killed" state), the phone's system still finds a way to tell you about important updates, like a neighbor calling your phone if something happened at your house.

Understanding these different kitchen "states" is super important. It means you can build apps that are polite when users are busy, giving them subtle updates, but also really good at getting their attention for truly important news, no matter what they're doing. So, when you build your own apps, you'll know how to make notifications super helpful without ever being annoying!

When building mobile applications, how your app responds to a push notification depends critically on its current state: foreground, background, or killed. Understanding these states is crucial for delivering a seamless user experience. In the foreground state, your app is actively open and visible to the user. When a notification arrives in this state, the operating system typically does not display its default visual alert (like a banner or sound). Instead, your application takes full control. This allows you to process the notification data silently, update UI elements, or present a custom, in-app notification experience (like a snackbar or a small banner within your app's existing layout) without interrupting the user's flow with a system-level alert. This direct control is key for subtle and context-aware interactions.

Next is the background state, where your app is minimized, suspended, or running but not actively visible on the screen. In this scenario, the operating system usually takes over the responsibility of displaying the notification to the user, typically in the notification shade, as a banner, or with a badge icon. However, your app can still receive and process the notification's data payload. For instance, you might update cached data, perform a quick sync, or prepare UI elements for when the user brings the app back to the foreground. If the user taps on the system-displayed notification, your app will be brought to the foreground, and the notification's payload will often be passed along, enabling you to deep link to specific content within the app.

Finally, the killed-state (also known as not-running or terminated state) occurs when your app is completely closed, either by the user force-quitting it or by the system to free up resources. When a push notification arrives for an app in this state, your app's code is not running to intercept it immediately. The operating system handles displaying the notification entirely. The critical difference here is that if the user taps on this notification, the app will be launched from scratch. Upon launch, the app can then access the notification's payload, allowing you to direct the user to a specific screen or initiate an action based on the notification content, effectively deep linking from a cold start. Consistent handling across all states ensures users can always access relevant content directly from a notification.

Key Takeaways

  • Notification handling logic varies significantly based on the app's current state: foreground, background, or killed.
  • In the foreground, your app has full control and typically presents custom in-app UI rather than system alerts.
  • In the background, the OS displays the notification, but your app can still process data and respond to taps.
  • In the killed-state, the OS handles display, and the app is launched from scratch upon tap, accessing the payload post-launch.
  • Implementing deep linking is crucial across all states for a consistent user experience, directing users to specific app content.

Code Example

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

// This handler is registered typically in your main.dart or a similar setup file
void setupFirebaseMessaging() {
  // Handle messages when the app is in the foreground
  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    print('Foreground message received!');
    print('Notification title: ${message.notification?.title}');
    print('Notification body: ${message.notification?.body}');
    print('Data payload: ${message.data}');

    // You can now display a custom in-app UI (e.g., a snackbar, dialog)
    // or process the data silently without interrupting the user.
    // Example: showToast(message.notification?.body ?? 'New notification!');
  });
}

How this code works

This code snippet's primary job is to manage push notifications specifically when the application is actively running and visible on the user's screen – a state known as the "foreground." Instead of relying on the device's operating system to automatically display a standard notification alert, this setup empowers the app to completely take over. When a push notification arrives while the app is in the foreground, this code intercepts it, allowing the application to decide precisely how to present the information to the user, perhaps with a custom in-app UI, or to silently process any underlying data without interruption.

The setupFirebaseMessaging function initiates this listening process by calling FirebaseMessaging.onMessage.listen. This listen method effectively sets up a continuous watch; whenever a new RemoteMessage arrives while the app is in the foreground, the code defined within the callback runs. Inside this callback, the message object provides all the notification's details, letting the code extract visible text like message.notification?.title and message.notification?.body, or access custom information sent from the server via message.data. A subtle but crucial point for beginners is that FirebaseMessaging.onMessage only handles messages when the app is in the foreground; for notifications received in background or killed states, different Firebase Messaging handlers are needed, and the system typically handles the initial display.