Skip to content

Unable to use onBackgroundMessage in Firebase Cloud Messaging #2387

Closed
@dvdlg

Description

@dvdlg

I am trying to implement an app with notifications. I followed the official instructions of firebase_messaging (https://pub.dev/packages/firebase_messaging), and everything worked as promissed, but there is something I don't understand. I was able to trigger onMessage, onLaunch and onResume and make the UI respond properly (showing the user the recieved message). But when onBackgroundMessage is triggered, I can only print the message to the debugging console. I looked all over the internet, but it seems like that's how all discussions end. What can I do inside onBackgroundMessage? I failed to find any way to show to the user that the message had been recieved. I tried to navigate using a global navigator key but navigatorKey.currentState is null while running in background. I tried to update a provider, but it was also null. I even tried to update a global variable but it was the old value again when the app resumed to foreground. Could anyone please publish code example in which onBackgroundMessage has any impact on the user? (not only printing to the console).

The reason that I really want to use onBackgroundMessage is because when I get the notification while running in background, onResume and onLaunch are triggered only if I click the notification. If I click the app icon, the notification stays and nothing is triggered. I have no way to tell the user about the notification. If someone finds a way to read notifications that the user didn't click yet, that would help even more.

This is my code. When pressing button 1, a notification is sent after a delay of 5 seconds (to let the user time to put the app in background). When pressing button 2, a data message is sent.
Uppon recieving the message the three counters are incremented. But in some cases nothing happens.

expected result
In all scenarios all three counters should increment.

Actual result
In case the app was running in background while the message was sent, after resuming to the app nothing happend (both with buttom 1 and 2).

import 'dart:convert';

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(MyApp());

class MyProvider extends StatefulWidget {
  final Widget child;
  const MyProvider({Key key, this.child}) : super(key: key);

  @override
  MyProviderState createState() => MyProviderState();
}

class MyProviderState extends State<MyProvider> {
  int counter = 0;

  void increment() {
    setState(() {
      counter += 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Provider.value(
      value: counter,
      child: Provider.value(
        value: this,
        child: widget.child,
      ),
    );
  }
}

const String serverToken = /* put your own server token here */;
final FirebaseMessaging firebaseMessaging = FirebaseMessaging();

int globalInt = 0;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final GlobalKey<MyProviderState> providerKey = GlobalKey<MyProviderState>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Rebuild MyApp');
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ]);
    SharedPreferences.getInstance().then((prefs) => prefs.setString('counter3', '0'));
    return MyProvider(
      key: providerKey,
      child: MaterialApp(
        title: 'Flutter Demo',
        navigatorKey: navigatorKey,
        home: HomePage(),
        routes: {},
      ),
    );
  }
}

Future<void> sendNotification(int buttonNumber) async {
  print('Pressed button $buttonNumber');
  // This delay is to give the user time to move the app to background.
  await Future.delayed(Duration(seconds: 5));
  await http.post(
    'https://fcm.googleapis.com/fcm/send',
    headers: <String, String>{
      'Content-Type': 'application/json',
      'Authorization': 'key=$serverToken',
    },
    body: jsonEncode(
      <String, dynamic>{
        'notification': buttonNumber == 1
            ? <String, dynamic>{
                'body': 'This is a body',
                'title': 'This is a title',
              }
            : <String, dynamic>{},
        'priority': 'high',
        'data': <String, dynamic>{
          'click_action': 'FLUTTER_NOTIFICATION_CLICK',
          'id': '1',
          'status': 'done',
          'action': buttonNumber,
        },
        'to': await firebaseMessaging.getToken(),
      },
    ),
  );
  print('Sent notification');
}

Future<void> handleNotification(Map<String, dynamic> message) async {
  int action = int.parse(message['data']['action']);
  print('>>> Got notification with action $action');
  print('>>> navigatorKey.currentState = ${navigatorKey.currentState}');
  print('>>> providerKey.currentState = ${providerKey.currentState}');
  if (navigatorKey.currentState != null) {
    print('>>> You are lucky! The app is in foreground or the user clicked the notification.');
    print('>>> You can update the UI or navigate to inform the user of the notification.');
  } else {
    print(
        '>>> You are helpless! onBackgroundMessage was triggered but there is nothing you can do to inform the user.');
  }
  // Increment counter1
  print('>>> trying to increment counter1 from $globalInt to ${globalInt + 1}');
  globalInt += 1;
  // Increment counter2
  try {
    print('>>> trying to increment counter2');
    providerKey.currentState.increment();
  } catch (e) {
    print('>>> Error: <<$e>>');
  }
  // Increment counter3
  try {
    print('>>> trying to increment counter3');
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final int value = int.parse(prefs.getString('counter3'));
    prefs.setString('counter3', '${value + 1}');
  } catch (e) {
    print('>>> Error: <<$e>>');
  }
}

Future<void> onBackgroundMessage(Map<String, dynamic> message) async {
  print('>>> onBackgroundMessage');
  await handleNotification(message);
}

Future<void> onMessage(Map<String, dynamic> message) async {
  print('>>> onMessage');
  await handleNotification(message);
}

Future<void> onResume(Map<String, dynamic> message) async {
  print('>>> onResume');
  await handleNotification(message);
}

Future<void> onLaunch(Map<String, dynamic> message) async {
  print('>>> onLaunch');
  await handleNotification(message);
}

class HomePage extends StatefulWidget {
  @override
  _HomePage createState() => _HomePage();
}

class _HomePage extends State<HomePage> {
  int counter1 = 0;
  int counter2 = 0;
  int counter3 = 0;

  @override
  void initState() {
    super.initState();
    firebaseMessaging.requestNotificationPermissions(
        const IosNotificationSettings(sound: true, badge: true, alert: true, provisional: false));
    firebaseMessaging.configure(
      onBackgroundMessage: onBackgroundMessage,
      onMessage: onMessage,
      onResume: onResume,
      onLaunch: onLaunch,
    );
  }

  @override
  Widget build(BuildContext context) {
    counter1 = globalInt;
    counter2 = Provider.of<int>(context);
    SharedPreferences.getInstance().then((prefs) {
      setState(() {
        counter3 = int.parse(prefs.getString('counter3'));
      });
    });
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: SizedBox(height: 50),
          ),
          Expanded(
            child: Text('$counter1', textScaleFactor: 3.5),
          ),
          Expanded(
            child: Text('$counter2', textScaleFactor: 3.5),
          ),
          Expanded(
            child: Text('$counter3', textScaleFactor: 3.5),
          ),
          Expanded(
            child: Row(children: [
              Expanded(
                child: FloatingActionButton(
                  onPressed: () => sendNotification(1),
                  child: Text('1', textScaleFactor: 2),
                ),
              ),
              Expanded(
                child: FloatingActionButton(
                  onPressed: () => sendNotification(2),
                  child: Text('2', textScaleFactor: 2),
                ),
              ),
            ]),
          ),
        ],
      ),
    );
  }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions