flutterfire icon indicating copy to clipboard operation
flutterfire copied to clipboard

🐛 [firebase_messaging] On Android `onMessageOpenedApp` not triggered when tap on push notification in example

Open LeGoffMael opened this issue 1 year ago • 8 comments

Bug report

Describe the bug The onMessageOpenedApp listener is not triggered in example app after opening incoming push notification (on Android only).

I am not sure if it is the expected behavior. I find it quite unclear right now on how to handle notification interaction when the app is in the foreground. On iOS it seems to be working just fine, on Android it is recommended to use flutter_local_notifications, should this plugin also handled the interaction ?

If so, i think the example app should provide the way-to-go solution to handle notification interaction.

https://user-images.githubusercontent.com/22376981/180719078-a86651e7-0a92-400d-b615-0be45a721025.mp4

Steps to reproduce

Steps to reproduce the behavior:

  1. Run example project at commit (328b40aadd7d7a5f3a2ee18e1bb6685bd9fb3552)

Expected behavior

When tapping on push notification in example app, onMessageOpenedApp listener should get triggered and so open MessageView screen.

Sample project

Example project at commit (328b40aadd7d7a5f3a2ee18e1bb6685bd9fb3552)

Just replaced main.dart line 40 by

await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

to be able to launch it


Additional context

Flutter 3.0.5

Tested and reproduced on both :

  • Samsung Galaxy S7 edge (Android 8.0.0)
  • Samsung Galaxy S8 (Android 9)

Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand
legoffmael@Maels-Mac-mini example % fvm flutter doctor                                                         
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.0.5, on macOS 12.3.1 21E258 darwin-arm, locale en-KR)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2020.3)
[✓] VS Code (version 1.69.2)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

• No issues found!

Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand
legoffmael@Maels-Mac-mini example % fvm flutter pub deps -- --style=compact
Dart SDK 2.17.6
Flutter SDK 3.0.5
firebase_messaging_example 0.0.0

dependencies:
- firebase_core 1.20.0 [firebase_core_platform_interface firebase_core_web flutter meta]
- firebase_messaging 12.0.1 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta]
- flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine]
- flutter_local_notifications 9.7.0 [clock flutter flutter_local_notifications_linux flutter_local_notifications_platform_interface timezone]
- http 0.13.4 [async http_parser meta path]

dependency overrides:
- firebase_core 1.20.0 [firebase_core_platform_interface firebase_core_web flutter meta]
- firebase_core_platform_interface 4.5.0 [collection flutter flutter_test meta plugin_platform_interface]
- firebase_core_web 1.7.1 [firebase_core_platform_interface flutter flutter_web_plugins js meta]
- firebase_messaging 12.0.1 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta]
- firebase_messaging_platform_interface 4.1.0 [firebase_core flutter meta plugin_platform_interface]
- firebase_messaging_web 3.1.0 [firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins js meta]
- plugin_platform_interface 2.1.2 [meta]

transitive dependencies:
- args 2.3.1
- async 2.8.2 [collection meta]
- boolean_selector 2.1.0 [source_span string_scanner]
- characters 1.2.0
- charcode 1.3.1
- clock 1.1.0
- collection 1.16.0
- dbus 0.7.7 [args ffi meta xml]
- fake_async 1.3.0 [clock collection]
- ffi 2.0.1
- file 6.1.2 [meta path]
- flutter_local_notifications_linux 0.5.0+1 [flutter flutter_local_notifications_platform_interface dbus path xdg_directories]
- flutter_local_notifications_platform_interface 5.0.0 [flutter plugin_platform_interface]
- flutter_test 0.0.0 [flutter test_api path fake_async clock stack_trace vector_math async boolean_selector characters charcode collection matcher material_color_utilities meta source_span stream_channel string_scanner term_glyph]
- flutter_web_plugins 0.0.0 [flutter js characters collection material_color_utilities meta vector_math]
- http_parser 4.0.0 [charcode collection source_span string_scanner typed_data]
- js 0.6.4
- matcher 0.12.11 [stack_trace]
- material_color_utilities 0.1.4
- meta 1.7.0
- path 1.8.1
- petitparser 5.0.0 [meta]
- platform 3.1.0
- process 4.2.4 [file path platform]
- sky_engine 0.0.99
- source_span 1.8.2 [collection path term_glyph]
- stack_trace 1.10.0 [path]
- stream_channel 2.1.0 [async]
- string_scanner 1.1.0 [charcode source_span]
- term_glyph 1.2.0
- test_api 0.4.9 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph matcher]
- timezone 0.8.0 [path]
- typed_data 1.3.0 [collection]
- vector_math 2.1.2
- xdg_directories 0.2.0+1 [meta path process]
- xml 6.1.0 [collection meta petitparser]

LeGoffMael avatar Jul 25 '22 07:07 LeGoffMael

onMessageOpenedApp.listen has stopped working on both Android & iOS for me at all, using latest stable flutter & firebase_messaging.

dsgriffin avatar Jul 25 '22 17:07 dsgriffin

on Android it is recommended to use flutter_local_notifications, should this plugin also handled the interaction ?

If so, i think the example app should provide the way-to-go solution to handle notification interaction.

@LeGoffMael The plugin example does make use of flutter_local_notifications and you can check its implementation and see if it helps in your case.

@dsgriffin Can you provide more details like plugin version, steps to replicate and current behavior you are seeing ?

darshankawar avatar Jul 26 '22 08:07 darshankawar

~~The plugin example does use it. See in the main.dart file of the example, line 13.~~ (sorry I misread your above comment)

It is explained in the documentation that flutter_local_notifications need to be use to display foreground notifications on android (https://firebase.flutter.dev/docs/messaging/notifications#notification-channels).

The fact that i cannot see the implementation of push notification interaction (in foreground), is the point of my first comment.

LeGoffMael avatar Jul 26 '22 08:07 LeGoffMael

@LeGoffMael Thanks for the update. Can you try below code sample and see if works for you ?

code sample

// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart=2.9

import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'dart:ui';

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

import 'message.dart';
import 'message_list.dart';
import 'permissions.dart';
import 'token_monitor.dart';

AndroidNotificationChannel channel;
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
const String isolateName = 'isolate';

/// Define a top-level named handler which background/terminated messages will
/// call.
///
/// To verify things are working, check out the native platform logs.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  ReceivePort receiver = ReceivePort();
  IsolateNameServer.registerPortWithName(receiver.sendPort, isolateName);

  receiver.listen((message) async {
    if (message == "stop") {
      await FlutterRingtonePlayer.stop();
    }
  });
  playAudio();
  await Firebase.initializeApp();
  print('Handling a background message ${message.messageId}');
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  // Set the background messaging handler early on, as a named top-level function
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  if (!kIsWeb) {
    /// Create a [AndroidNotificationChannel] for heads up notifications
    channel = AndroidNotificationChannel(
      'high_importance_channel', // id
      'High Importance Notifications', // title
      description:
          'This channel is used for important notifications.', // description
      importance: Importance.high,
    );

    /// Initialize the [FlutterLocalNotificationsPlugin] package.
    flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

    /// Create an Android Notification Channel.
    ///
    /// We use this channel in the `AndroidManifest.xml` file to override the
    /// default FCM channel to enable heads up notifications.
    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(channel);

    /// Update the iOS foreground notification presentation options to allow
    /// heads up notifications.
    await FirebaseMessaging.instance
        .setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
  }

  runApp(MessagingExampleApp());
}

/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Messaging Example App',
      theme: ThemeData.dark(),
      routes: {
        '/': (context) => Application(),
        '/message': (context) => MessageView(),
      },
    );
  }
}

// Crude counter to make messages unique
int _messageCount = 0;

/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String token) {
  _messageCount++;
  return jsonEncode({
    'to': token,
    'data': {
      'via': 'FlutterFire Cloud Messaging!!!',
      'count': _messageCount.toString(),
    },
    'notification': {
      'title': 'Hello FlutterFire!',
      'body': 'This notification (#$_messageCount) was created via FCM!',
    },
  });
}

void playAudio() {
  FlutterRingtonePlayer.play(
    android: AndroidSounds.ringtone,
    ios: const IosSound(1023),
    looping: false,
    volume: 0.8,
  );
}

Future<void> stopAudio() async {
  IsolateNameServer.lookupPortByName(isolateName)?.send("stop");
  await FlutterRingtonePlayer.stop();
}

/// Renders the example application.
class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  String _token;

  @override
  void initState() {
    super.initState();
    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage message) {
      if (message != null) {
        print("Got initial message $message");
        stopAudio();
      }
    });

    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification notification = message.notification;
      AndroidNotification android = message.notification?.android;
      print('A new onMessage event was published!');
      playAudio();
      if (notification != null && android != null && !kIsWeb) {
        flutterLocalNotificationsPlugin.show(
            notification.hashCode,
            notification.title,
            notification.body,
            NotificationDetails(
              android: AndroidNotificationDetails(
                channel.id,
                channel.name,
                channelDescription: channel.description,
                // TODO add a proper drawable resource to android, for now using
                //      one that already exists in example app.
                icon: 'launch_background',
              ),
            ));
      }
    });

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      print('App launched!');
      stopAudio();
    });
  }

  Future<void> sendPushMessage() async {
    if (_token == null) {
      print('Unable to send FCM message, no token exists.');
      return;
    }

    try {
      var resp = json.decode((await http.post(
        Uri.parse('https://fcm.googleapis.com/fcm/send'),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization':
              'key=AAAAcVC6H8o:APA91bF9eBcw6f4RbqJ89uYUGJ7RpMm2w0-yU8T8TIJ5LgXbjc91z_WxlOOzsUotZc6kYpqIQs7EnO5Im00xrLNuLpbkZ7VHgtFQ_Jv19W-XMi2dvWLteOZDTo77u8SzPHy-qF1bxBgp'
        },
        body: constructFCMPayload(_token),
      ))
          .body);
      print(resp);
      if (resp['failure'] == 1) {
        print(
            "Failed to send to $_token due to ${resp['results'][0]['error']}");
      }
    } catch (e) {
      print(e);
    }
  }

  Future<void> onActionSelected(String value) async {
    switch (value) {
      case 'subscribe':
        {
          print(
              'FlutterFire Messaging Example: Subscribing to topic "fcm_test".');
          await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
          print(
              'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.');
        }
        break;
      case 'unsubscribe':
        {
          print(
              'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".');
          await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
          print(
              'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.');
        }
        break;
      case 'get_apns_token':
        {
          if (defaultTargetPlatform == TargetPlatform.iOS ||
              defaultTargetPlatform == TargetPlatform.macOS) {
            print('FlutterFire Messaging Example: Getting APNs token...');
            String token = await FirebaseMessaging.instance.getAPNSToken();
            print('FlutterFire Messaging Example: Got APNs token: $token');
          } else {
            print(
                'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.');
          }
        }
        break;
      default:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Cloud Messaging'),
        actions: <Widget>[
          PopupMenuButton(
            onSelected: onActionSelected,
            itemBuilder: (BuildContext context) {
              return [
                const PopupMenuItem(
                  value: 'subscribe',
                  child: Text('Subscribe to topic'),
                ),
                const PopupMenuItem(
                  value: 'unsubscribe',
                  child: Text('Unsubscribe to topic'),
                ),
                const PopupMenuItem(
                  value: 'get_apns_token',
                  child: Text('Get APNs token (Apple only)'),
                ),
              ];
            },
          ),
        ],
      ),
      floatingActionButton: Builder(
        builder: (context) => FloatingActionButton(
          onPressed: () {
            playAudio();
            // FlutterRingtonePlayer.stop();

            // sendPushMessage,
          },
          backgroundColor: Colors.white,
          child: const Icon(Icons.send),
        ),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            MetaCard('Permissions', Permissions()),
            MetaCard('FCM Token', TokenMonitor((token) {
              print('Token monitor set $_token');
              _token = token;
              return Column(
                children: [
                  token == null
                      ? const CircularProgressIndicator()
                      : Text(token, style: const TextStyle(fontSize: 12)),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        onPressed: () async {
                          var before = _token;
                          print('Before: $_token');
                          var token;
                          if (kIsWeb) {
                            token = await FirebaseMessaging.instance.getToken(
                                vapidKey:
                                    "BHOPmb4Gz7mSLtue-BSzZGOzIO1LTiHThiHl1_ZbUtRaj5PhHJhR2Isr9hGBH1gfw4jhcKJVTyDPneau8kdLyVw");
                          } else {
                            token = await FirebaseMessaging.instance.getToken();
                          }
                          _token = token;
                          print('After: $_token');
                          if (_token == null) {
                            ScaffoldMessenger.of(context).showSnackBar(
                                SnackBar(content: const Text("No token!")));
                            return;
                          }
                          if (before == _token) {
                            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                content:
                                    const Text("Current token is valid!")));
                          } else {
                            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                content: const Text("Got new token!")));
                          }
                        },
                        icon: const Icon(Icons.refresh),
                        tooltip: "Get token",
                      ),
                      if (_token != null)
                        IconButton(
                          onPressed: () {
                            Clipboard.setData(ClipboardData(text: _token));
                            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                content:
                                    const Text("Copied token to clipboard!")));
                          },
                          icon: const Icon(Icons.copy),
                          tooltip: "Copy token to clipboard",
                        ),
                      if (_token != null)
                        IconButton(
                          onPressed: () async {
                            await FirebaseMessaging.instance.deleteToken();
                            print('Deleted token $_token');
                            var token =
                                await FirebaseMessaging.instance.getToken();
                            print("New token $token");
                            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                content: const Text("Deleted token!")));
                          },
                          icon: const Icon(Icons.delete_forever),
                          tooltip: "Delete token",
                        ),
                    ],
                  ),
                ],
              );
            })),
            // MetaCard('Message Stream', MessageList()),
          ],
        ),
      ),
    );
  }
}

/// UI Widget for displaying metadata.
class MetaCard extends StatelessWidget {
  final String _title;
  final Widget _children;

  // ignore: public_member_api_docs
  MetaCard(this._title, this._children);

  @override
  Widget build(BuildContext context) {
    return Container(
        width: double.infinity,
        margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
        child: Card(
            child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(children: [
                  Container(
                      margin: const EdgeInsets.only(bottom: 16),
                      child:
                          Text(_title, style: const TextStyle(fontSize: 18))),
                  _children,
                ]))));
  }
}

darshankawar avatar Jul 27 '22 11:07 darshankawar

Thank you for the code sample~

Unfortunately i don't seem to be receiving any foreground push notification using it.

https://user-images.githubusercontent.com/22376981/181425474-1ad310b3-1e5a-491f-8929-e4e8f61c0893.mp4

LeGoffMael avatar Jul 28 '22 05:07 LeGoffMael

Can you take a look at this issue and underlying comments / code and see if it helps ? https://github.com/firebase/flutterfire/issues/7243

darshankawar avatar Jul 28 '22 09:07 darshankawar

Sorry but I don't see what part of this issue can help me. Receiving notifications is not a problem, but clicking on them to interact with them is.

I tried to use the "solution" described in the issue but it does not changes anything

code sample
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:convert';

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

import 'message.dart';
import 'message_list.dart';
import 'permissions.dart';
import 'token_monitor.dart';
import 'firebase_options.dart';

/// Define a top-level named handler which background/terminated messages will
/// call.
///
/// To verify things are working, check out the native platform logs.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  print('Handling a background message ${message.messageId}');
}

/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;

/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  // Set the background messaging handler early on, as a named top-level function
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  if (!kIsWeb) {
    channel = const AndroidNotificationChannel(
      'high_importance_channel', // id
      'High Importance Notifications', // title
      description:
          'This channel is used for important notifications.', // description
      importance: Importance.high,
    );

    flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

    /// Create an Android Notification Channel.
    ///
    /// We use this channel in the `AndroidManifest.xml` file to override the
    /// default FCM channel to enable heads up notifications.
    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(channel);

    /// Update the iOS foreground notification presentation options to allow
    /// heads up notifications.
    await FirebaseMessaging.instance
        .setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
  }

  runApp(MessagingExampleApp());
}

/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Messaging Example App',
      theme: ThemeData.dark(),
      routes: {
        '/': (context) => Application(),
        '/message': (context) => MessageView(),
      },
    );
  }
}

// Crude counter to make messages unique
int _messageCount = 0;

/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String? token) {
  _messageCount++;
  return jsonEncode({
    'token': token,
    'data': {
      'via': 'FlutterFire Cloud Messaging!!!',
      'count': _messageCount.toString(),
    },
    'notification': {
      'title': 'Hello FlutterFire!',
      'body': 'This notification (#$_messageCount) was created via FCM!',
    },
  });
}

/// Renders the example application.
class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  String? _token;

  StreamSubscription<RemoteMessage>? _messageListenerStream;

  @override
  void initState() {
    super.initState();
    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage? message) {
      if (message != null) {
        Navigator.pushNamed(
          context,
          '/message',
          arguments: MessageArguments(message, true),
        );
      }
    });

    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification? notification = message.notification;
      AndroidNotification? android = message.notification?.android;
      if (notification != null && android != null && !kIsWeb) {
        flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              channelDescription: channel.description,
              // TODO add a proper drawable resource to android, for now using
              //      one that already exists in example app.
              icon: 'launch_background',
            ),
          ),
        );
      }
    });

    if (_messageListenerStream == null) {
      _messageListenerStream =
          FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
        print('First - A new onMessageOpenedApp event was published!');
        Navigator.pushNamed(
          context,
          '/message',
          arguments: MessageArguments(message, true),
        );
      });
    } else {
      _messageListenerStream!.onData((RemoteMessage? message) {
        print('Second - A new onMessageOpenedApp event was published!');
        if (message != null) {
          Navigator.pushNamed(
            context,
            '/message',
            arguments: MessageArguments(message, true),
          );
        }
      });
    }
  }

  Future<void> sendPushMessage() async {
    if (_token == null) {
      print('Unable to send FCM message, no token exists.');
      return;
    }

    try {
      await http.post(
        Uri.parse('https://api.rnfirebase.io/messaging/send'),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: constructFCMPayload(_token),
      );
      print('FCM request for device sent!');
    } catch (e) {
      print(e);
    }
  }

  Future<void> onActionSelected(String value) async {
    switch (value) {
      case 'subscribe':
        {
          print(
            'FlutterFire Messaging Example: Subscribing to topic "fcm_test".',
          );
          await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
          print(
            'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.',
          );
        }
        break;
      case 'unsubscribe':
        {
          print(
            'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".',
          );
          await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
          print(
            'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.',
          );
        }
        break;
      case 'get_apns_token':
        {
          if (defaultTargetPlatform == TargetPlatform.iOS ||
              defaultTargetPlatform == TargetPlatform.macOS) {
            print('FlutterFire Messaging Example: Getting APNs token...');
            String? token = await FirebaseMessaging.instance.getAPNSToken();
            print('FlutterFire Messaging Example: Got APNs token: $token');
          } else {
            print(
              'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.',
            );
          }
        }
        break;
      default:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Cloud Messaging'),
        actions: <Widget>[
          PopupMenuButton(
            onSelected: onActionSelected,
            itemBuilder: (BuildContext context) {
              return [
                const PopupMenuItem(
                  value: 'subscribe',
                  child: Text('Subscribe to topic'),
                ),
                const PopupMenuItem(
                  value: 'unsubscribe',
                  child: Text('Unsubscribe to topic'),
                ),
                const PopupMenuItem(
                  value: 'get_apns_token',
                  child: Text('Get APNs token (Apple only)'),
                ),
              ];
            },
          ),
        ],
      ),
      floatingActionButton: Builder(
        builder: (context) => FloatingActionButton(
          onPressed: sendPushMessage,
          backgroundColor: Colors.white,
          child: const Icon(Icons.send),
        ),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            MetaCard('Permissions', Permissions()),
            MetaCard(
              'FCM Token',
              TokenMonitor((token) {
                _token = token;
                return token == null
                    ? const CircularProgressIndicator()
                    : Text(token, style: const TextStyle(fontSize: 12));
              }),
            ),
            MetaCard('Message Stream', MessageList()),
          ],
        ),
      ),
    );
  }
}

/// UI Widget for displaying metadata.
class MetaCard extends StatelessWidget {
  final String _title;
  final Widget _children;

  // ignore: public_member_api_docs
  MetaCard(this._title, this._children);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
      child: Card(
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            children: [
              Container(
                margin: const EdgeInsets.only(bottom: 16),
                child: Text(_title, style: const TextStyle(fontSize: 18)),
              ),
              _children,
            ],
          ),
        ),
      ),
    );
  }
}

LeGoffMael avatar Jul 29 '22 03:07 LeGoffMael

Thanks for the update. I tried the same using code sample share earlier and do see same behavior.

darshankawar avatar Jul 29 '22 10:07 darshankawar

Any update on this?

MerryOscar avatar Mar 10 '23 09:03 MerryOscar

A recent issue with same report: https://github.com/firebase/flutterfire/issues/10517

Tagging @russellwheatley for thoughts.

darshankawar avatar Mar 13 '23 11:03 darshankawar

any update?

leandrormartins avatar Mar 27 '23 03:03 leandrormartins

For me, solution was to add FirebaseMessaging.instance.getInitialMessage().then((message) {...}); I haven't removed onMessageOpenedApp but after adding getInitialMessage both use-cases (notification taps when in background and when terminated) work just fine.

pavelsg avatar Apr 05 '23 07:04 pavelsg

Having same issue

My code for initialMessage

RemoteMessage? initialMessage =
        await FirebaseMessaging.instance.getInitialMessage().then((value) {
      print('on get init message' + value.toString());
      if (value != null) {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => HelpPage(
              showAppbar: true,
            ),
          ),
        );
      }
    });

Code for onMessageOpenedApp

FirebaseMessaging.onMessageOpenedApp.listen(
      onDone: () {
        print('done');
      },
      onError: (error) {
        print('error ' + error.toString());
      },
      (remoteMessage) {
        _handleMessage(remoteMessage);
      },
    );

Any update on this? Or at least a tested workaround?

Thanks in advance.

niladri-raychaudhuri avatar May 07 '23 10:05 niladri-raychaudhuri

For me, solution was to add FirebaseMessaging.instance.getInitialMessage().then((message) {...}); I haven't removed onMessageOpenedApp but after adding getInitialMessage both use-cases (notification taps when in background and when terminated) work just fine.

does not work for me. @pavelsg could you please share the code snippet?

niladri-raychaudhuri avatar May 07 '23 10:05 niladri-raychaudhuri

Finally this has worked for me. I am now able to open the App while in background or in terminated state, by tapping the notification that appeared on the System Tray

THE FIX:

The <intent-filter> was missing from my AndroidManifest.xml

 <intent-filter>
       <action android:name="FLUTTER_NOTIFICATION_CLICK"/>
       <category android:name="android.intent.category.DEFAULT"/>
 </intent-filter>

The name FLUTTER_NOTIFICATION_CLICK needs to be mentioned in the message payload as below:

 const payLoad = {
            data: { 'type': 'Emergency Message' },
            notification: {
                title: `Emergency Message`,
                body: `${data.message} ${data.currentLocation}`,
                clickAction: 'FLUTTER_NOTIFICATION_CLICK' // This click action name should be the action name in the AndroidManifest.xml
            }
        };
   return fcm.sendToDevice(tokens, payLoad);

niladri-raychaudhuri avatar May 08 '23 02:05 niladri-raychaudhuri

From https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

Screenshot 2023-05-23 at 21 53 07

Probably you're sending a "data" type message which FCM is not handling, you have to handle it manually. to do so, add a "notification" field to your message.

Like so:

"notification":{
  "title":"Portugal vs. Denmark",
  "body":"great match!"
}

This will work for both cases https://firebase.google.com/docs/cloud-messaging/concept-options#notification-messages-with-optional-data-payload

theiskaa avatar May 23 '23 17:05 theiskaa

I've just checked using the messaging example app on FlutterFire, it does work which suggests you're missing a setup step. Please use the example app here, remove the android service file in android/app/google-services.json and run flutterfire configure with your own project configuration. Let me know if this works.

russellwheatley avatar Jul 03 '23 16:07 russellwheatley

Hey @LeGoffMael. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot avatar Jul 12 '23 01:07 google-oss-bot

Since there haven't been any recent updates here, I am going to close this issue.

@LeGoffMael if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.

google-oss-bot avatar Jul 21 '23 01:07 google-oss-bot