Skip to content

Software Development at Program Tom LTD

Place for coding, programming, development and software in general.

Menu
  • Blog
  • PDF Booklets
  • Dev Utils & Content
  • Java Spring Boot Or Web Apps
  • English
    • български
    • English
    • Español
    • Português
    • हिन्दी
    • Русский
    • Deutsch
    • Français
    • Italiano
    • العربية
  • About Us
  • Flutter Apps
Menu
Firebase Messaging Background Message

Firebase Messaging – on Background Message

Posted on April 28, 2025May 10, 2025 by Toma Velev

In Flutter, particularly when using firebase_messaging, the FirebaseMessaging -> on Background Message -> handler cannot be run inside a custom Zone in the traditional sense like how you might wrap code in runZonedGuarded. Here’s why and what you can (and cannot) do:


📌 Why it doesn’t run in a custom Zone

FirebaseMessaging.onBackgroundMessage is executed in a separate Dart isolate, not in the main isolate where your app’s UI and main logic live. Since a Zone is tied to an isolate, the custom zone you define in the main isolate won’t apply to the background isolate.

This means:

  • No access to variables, context, or zones from your main isolate.
  • You cannot catch errors from onBackgroundMessage using your app’s runZonedGuarded setup.
  • You cannot use most of your app’s dependencies (like Provider, GetIt, etc.) directly inside it.

✅ What you can do instead

  1. Keep it simple: The onBackgroundMessage function should do minimal work (like logging or scheduling a local notification).
  2. Isolate-safe code: Avoid using classes or dependencies that rely on the main isolate. Stick to basic Dart functionality.
  3. Error handling: Use try/catch within the onBackgroundMessage itself, since you can’t rely on zone-level error capture:
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  try {
    await Firebase.initializeApp(); // Required if not already done
    print('Handling a background message: ${message.messageId}');
    // Do your isolate-safe background processing here
  } catch (e, stack) {
    print('Error in background handler: $e\n$stack');
  }
}

If you’re trying to achieve something specific with a custom zone (e.g. enhanced logging, error tracking), maybe I can suggest an alternate approach. What’s your goal with the custom zone?

the main flutter app runs in a zone – coded by the previous developer

Ah, I see — so your main Flutter app is wrapped in a custom Zone, likely for logging, error reporting, or controlling async behavior. That’s a solid move for debugging and observability.

But here’s the catch again:


🔒 FirebaseMessaging.onBackgroundMessage runs in a different isolate

Even though your main app runs in a custom zone, that zone has zero effect on the background message handler, because:

  • It’s spun up in a separate isolate by the firebase_messaging plugin.
  • Zones do not cross isolate boundaries.

So even if the previous developer did something like:

runZonedGuarded(() {
  runApp(MyApp());
}, (error, stack) {
  logErrorToSentry(error, stack);
});

That zone is only active in the main isolate where your app UI and lifecycle live.


🚫 So, can you use that zone in onBackgroundMessage?

No, you cannot directly use the same zone — and even trying to re-create it manually in the background handler won’t work the same way because:

  • You don’t have access to shared context from the main isolate.
  • You’d need to manually bootstrap everything needed inside that isolate (which often isn’t worth the complexity unless you’re doing serious background work).

✅ What can you do in the background handler?

  • Reinitialize Firebase if needed.
  • Catch and log errors locally with try/catch.
  • Use low-dependency, stateless logic (e.g., saving to shared preferences, sending a local notification).

If you really need background logic to report errors or interact with services from your main zone (like custom logging), you could:

  1. Use a custom logging function inside the handler (keep it isolate-safe).
  2. Persist data (e.g., to a file or SharedPreferences), and then have your main app read and report it when it launches again.

Would it help if I showed a pattern for doing error logging or forwarding messages from the background handler to your app’s main zone once it resumes?

How to properly initialize FirebaseMessaging

To properly initialize FirebaseMessaging in a Flutter app, you’ll want to cover three key environments:

  1. 🔧 Main isolate (foreground & background)
  2. 🌒 Background message handler (separate isolate)
  3. 📱 iOS/Android configuration

Here’s a complete step-by-step guide to setting it up the right way:


✅ 1. Add dependencies

In pubspec.yaml:

dependencies:
  firebase_core: ^latest
  firebase_messaging: ^latest

Run: flutter pub get


✅ 2. Initialize Firebase in main()

Wrap everything in runZonedGuarded if you’re using a custom zone.

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

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  runApp(MyApp());
}

✅ 3. Background handler

This must be a top-level function or a static method (no closures or class methods).

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp(); // Required here too!
  print('🔙 Background message: ${message.messageId}');
}

✅ 4. Request permissions (iOS only)

Inside your widget (like initState() of MyApp):

FirebaseMessaging messaging = FirebaseMessaging.instance;

NotificationSettings settings = await messaging.requestPermission(
  alert: true,
  badge: true,
  sound: true,
);

print('🔔 User granted permission: ${settings.authorizationStatus}');

✅ 5. Listen to messages

In the main isolate:

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print('📬 Message received in foreground: ${message.notification?.title}');
});

FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
  print('📱 User tapped on notification: ${message.notification?.title}');
});

✅ 6. Optional: Get device token

String? token = await FirebaseMessaging.instance.getToken();
print('📱 FCM Token: $token');

✅ 7. Android & iOS platform setup

  • On Android: Add google-services.json to android/app.
  • On iOS: Add GoogleService-Info.plist to Runner/, update permissions in Info.plist.

The Flutter bindings were initialized in a different zone than is now being used.

“The Flutter bindings were initialized in a different zone than is now being used.”

…means you’re running Flutter in a custom Zone, but some part of your app (or a plugin) is executing code outside that zone, causing zone inconsistency.


💡 Why this happens

Flutter’s WidgetsFlutterBinding.ensureInitialized() captures the current zone at the time it’s called — and Flutter expects to stay in that zone for lifecycle events.

If later code (like FirebaseMessaging.onBackgroundMessage, timers, or some plugin) runs in another zone or isolate, it causes that warning or crash.


✅ Fix it the right way

To ensure Flutter bindings and your app run in the same custom zone, you must:

  1. Initialize the bindings inside your custom runZonedGuarded.
  2. Avoid initializing anything before the zone is set.

🔧 Correct pattern:

void main() {
  runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();

    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

    runApp(MyApp());
  }, (error, stackTrace) {
    // Handle errors from within the zone
    print('❗ Uncaught error in zone: $error');
  });
}

⚠️ If you call WidgetsFlutterBinding.ensureInitialized() before runZonedGuarded, the bindings will attach to the root zone — and later zone-specific operations will trigger this mismatch.


🧪 Double-check

Make sure:

  • You’re not importing or triggering any Flutter logic before runZonedGuarded.
  • You’re not calling runApp() outside the zone.

Would you like me to review your main.dart and pinpoint where the zone mismatch might be happening?

What firebaseMessagingBackgroundHandler’ from native code, it must be annotated means

Great question — you’re seeing a message like this:

“The ‘firebaseMessagingBackgroundHandler’ must be annotated because it is invoked from native code.”

This means your Dart function _firebaseMessagingBackgroundHandler is being called by platform (native) code — likely from Android or iOS — when a background FCM notification arrives. Dart needs to make sure it knows how to preserve and locate this function when the app is not active.


✅ The fix: Add @pragma('vm:entry-point')

You must annotate the handler function like this:

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print('📩 Handling background message: ${message.messageId}');
}

💡 Why this is needed

In Flutter’s tree shaking and AOT compilation, unused functions may get stripped out — especially when the app is in the background or terminated. The @pragma('vm:entry-point') tells the Dart VM:

“Hey! Don’t strip this out — native code will need to call it.”


📌 Summary

  • Always annotate your background FCM handler like this:
    @pragma('vm:entry-point')
  • Make sure it’s a top-level function (not inside a class).
  • Add Firebase.initializeApp() inside the handler.

Here are some helpful links:

  • https://stackoverflow.com/questions/65864972/flutter-fcm-a-background-message-could-not-be-handled-in-dart-as-no-onbackgroun
  • https://github.com/firebase/flutterfire/issues/8215
  • https://stackoverflow.com/questions/79158012/dependency-flutter-local-notifications-requires-core-library-desugaring-to-be
  • https://pub.dev/packages/flutter_local_notifications#-android-setup
  • https://www.youtube.com/watch?v=uKz8tWbMuUw
  • https://programtom.com/dev/2020/10/07/social-software-8-features-to-make-a-product-successful/
  • Example of GridView Builder in Flutter
  • How to Visualize Listview inside Listview in Flutter
  • What other usages you know about public private cryptography
  • Get a Flutter App to Production
  • Firebase Dynamic Links Deprecation – migrating out to Java

Categories

  • Apps (20)
  • ChatGPT (19)
  • Choosing a Framework (38)
  • Flutter (201)
  • Graphical User Interface (13)
  • Marketing (113)
  • Software Development (268)
  • Spring (41)
  • StartUp (21)
  • Uncategorized (15)
  • Uncategorized (4)
  • Vaadin (14)

Tags

Algorithms (9) crypto (29) flutterdev (39) General (86) Java (7) QR & Bar Codes (3) Software Dev Choices (33) Spring Boot (1) standards (1) Theme (3) User Authentication & Authorization (9) User Experience (10) Utilities (19) WordPress (11)

Product categories

  • All Technologies (83)
    • Flutter Apps (23)
    • GPT (4)
    • Java (38)
    • Native Android (3)
    • PHP (9)
    • Spring (Boot) / Quarkus (35)
    • Utils (15)
    • Vaadin 24+ (27)
    • Vaadin 8 (1)
  • Apps (18)
    • Employees DB (1)
    • Notes (6)
    • Personal Budget (1)
    • Recipes Book (1)
    • Stuff Organizer (1)
    • To-Do (2)
  • PDF Books (3)
  • Source Code Generators (8)

Recent Posts

  • Example of GridView Builder in Flutter
  • How to Visualize Listview inside Listview in Flutter
  • What other usages you know about public private cryptography
  • Get a Flutter App to Production
  • Firebase Dynamic Links Deprecation – migrating out to Java

Post Categories

  • Apps (20)
  • ChatGPT (19)
  • Choosing a Framework (38)
  • Flutter (201)
  • Graphical User Interface (13)
  • Marketing (113)
  • Software Development (268)
  • Spring (41)
  • StartUp (21)
  • Uncategorized (15)
  • Uncategorized (4)
  • Vaadin (14)