最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android - Flutter FCM: Custom Sound Plays Alongside Default Sound & Redirection Fails When App is Terminated - Stack Ove

programmeradmin2浏览0评论
  1. I'm using Firebase Cloud Messaging (FCM) in my Flutter app and trying to achieve the following:

  2. Play a custom notification sound when a notification is received. Redirect to a specific screen when the notification is clicked, even if the app is terminated or not running in the background.

Issues:

  1. Custom Sound Problem:

    • When I include the notification object in the payload, the notification plays the default sound instead of the custom sound.
    • If I include both notification and custom sound configuration in the android section, I receive two notifications:
      • One with the default sound.
      • Another with the custom sound.
  2. Redirection Problem:

    • When I remove the notification object and rely entirely on the data object, the custom sound works, but redirection fails if the app is terminated or not in the background.
    • The app doesn't receive the notification click event in such cases.

Payload I'm Using:

Below is the payload I'm sending to FCM:
    {  
     "message": {
       "token": "DEVICE_FCM_TOKEN",  
       "notification": {  
         "title": "Visitor has been admitted!",  
         "body": "Dhaval developer (Visitor) has been admitted.",
       },  
      "android": {  
        "notification": {  
          "sound": "visitor_notification_sound"  
        }  
      },
      "apns":{
        "payload":{
            "aps":{
                "sound":"visitor_notification_sound.mp3"
            }
        }
      },  
      "data": {  
         "id": "1215454",  
         "notification_type": "visitor_visited",  
         "other_data_key": "other_data_value"  
     }  
  }  
}

Observations:

  1. With the notification object:

    • Redirection works even when the app is terminated or not in the background.
    • The notification plays the default sound, not the custom sound.
  2. Without the notification object:

    • The custom sound works fine.
    • Redirection fails when the app is terminated or not in the background (click events are not received).
  3. Including both notification and custom sound in the android section:

    • Two notifications are received:
      • One with the default sound.
      • Another with the custom sound.

Flutter Code: Here’s how I’m handling notifications in my Flutter app:

main.dart

import 'package:flutter/material.dart';
import 'package:notification_demo/fcm_controller.dart';
import 'package:notification_demo/firebase_options.dart';

Future<void> main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 await FirebaseCloudMessagingService().initFCM();
 runApp(const MyApp());
}

class MyApp extends StatefulWidget {
 const MyApp({super.key});

 @override
 State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
 @override
 Widget build(BuildContext context) {
   return const MaterialApp(
     home: Scaffold(
       body: Center(
         child: Text('hello'),
       ),
     ),
   );
 }
}

fcm_controller.dart

import 'dart:convert';
import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'dart:math' as math;
import 'package:notification_demo/firebase_options.dart';

@pragma('vm:entry-point')
Future<void> notificationTapBackground(NotificationResponse notificationResponse) async {
 debugPrint('background notification tap');
}

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
 await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 debugPrint("Handling a background message ${message.messageId}");
 if (message.messageId != null && message.messageId!.isNotEmpty) {
   FirebaseCloudMessagingService.showNotification(message);
 }
}

class FirebaseCloudMessagingService {
 Future<void> initFCM() async {
   debugPrint('DDDD initFCM');
   try {
     await _requestPermission();
     await _initNotificationInfo();
     String deviceToken = await _getToken() ?? '';
     debugPrint('FCM Token: $deviceToken');
     await _setupMessageHandlers();
     FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
   } catch (e) {
     log("Exception: $e");
   }
 }

 Future<void> _requestPermission() async {
   NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
     alert: true,
     announcement: true,
     badge: true,
     carPlay: true,
     criticalAlert: true,
     provisional: true,
     sound: true,
   );
   debugPrint('Permission status: ${settings.authorizationStatus}');
 }

 Future<void> _initNotificationInfo() async {
   var initializationSettingAndroid = const AndroidInitializationSettings('@mipmap/ic_launcher');
   const DarwinInitializationSettings initializationSettingIOS = DarwinInitializationSettings();
   var initializationSettings = InitializationSettings(android: initializationSettingAndroid, iOS: initializationSettingIOS);
   await flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) async {
     handleNotificationTappedFormNotificationTray(jsonDecode(notificationResponse.payload ?? "{}"));
   }, onDidReceiveBackgroundNotificationResponse: notificationTapBackground);
 }

 Future<String?> _getToken() async {
   try {
     return await FirebaseMessaging.instance.getToken();
   } catch (e) {
     debugPrint("Error fetching token: $e");
     return null;
   }
 }

 Future<void> _setupMessageHandlers() async {
   FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
     showNotification(message);
   });
   FirebaseMessaging.onMessageOpenedApp.listen((event) async {
     await handleNotificationTappedFormNotificationTray(event.data);
   });
 }

 static Future<void> showNotification(RemoteMessage message) async {
   String title = message.data['title'] ?? '';
   String body = message.data['body'] ?? '';
   String soundName = 'notification_sound_android';
   String iosSoundName = 'notification_sound_android.mp3';
   if (message.data['notification_type'] == 'visitor_visited') {
     soundName = 'visitor_notification_sound';
     iosSoundName = 'visitor_notification_sound.mp3';
   }
   AndroidNotificationChannel channel = AndroidNotificationChannel(
     soundName,
     'General Notifications',
     importance: Importance.max,
     playSound: true,
     sound: RawResourceAndroidNotificationSound(soundName),
   );
   AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
     channel.id,
     channel.name,
     sound: RawResourceAndroidNotificationSound(soundName),
   );
   NotificationDetails notificationDetails = NotificationDetails(
     android: androidNotificationDetails,
     iOS: DarwinNotificationDetails(sound: iosSoundName),
   );
   flutterLocalNotificationsPlugin.show(
     math.Random().nextInt(100000),
     title,
     body,
     notificationDetails,
     payload: jsonEncode(message.data),
   );
 }

 Future<void> handleNotificationTappedFormNotificationTray(Map<String, dynamic> notificationData) async {
   debugPrint('Notification tapped: $notificationData');
   // Implement redirection logic here
 }
}

Question: How can I configure FCM and handle notifications in Flutter so that:

  1. Custom sound plays without triggering the default sound or duplicating notifications.
  2. Clicking the notification redirects to a specific screen, even if the app is terminated or not running in the background.
  3. Only one notification is displayed.

Is there a way to resolve the conflict between the notification and data objects to achieve the desired behavior?

  1. I'm using Firebase Cloud Messaging (FCM) in my Flutter app and trying to achieve the following:

  2. Play a custom notification sound when a notification is received. Redirect to a specific screen when the notification is clicked, even if the app is terminated or not running in the background.

Issues:

  1. Custom Sound Problem:

    • When I include the notification object in the payload, the notification plays the default sound instead of the custom sound.
    • If I include both notification and custom sound configuration in the android section, I receive two notifications:
      • One with the default sound.
      • Another with the custom sound.
  2. Redirection Problem:

    • When I remove the notification object and rely entirely on the data object, the custom sound works, but redirection fails if the app is terminated or not in the background.
    • The app doesn't receive the notification click event in such cases.

Payload I'm Using:

Below is the payload I'm sending to FCM:
    {  
     "message": {
       "token": "DEVICE_FCM_TOKEN",  
       "notification": {  
         "title": "Visitor has been admitted!",  
         "body": "Dhaval developer (Visitor) has been admitted.",
       },  
      "android": {  
        "notification": {  
          "sound": "visitor_notification_sound"  
        }  
      },
      "apns":{
        "payload":{
            "aps":{
                "sound":"visitor_notification_sound.mp3"
            }
        }
      },  
      "data": {  
         "id": "1215454",  
         "notification_type": "visitor_visited",  
         "other_data_key": "other_data_value"  
     }  
  }  
}

Observations:

  1. With the notification object:

    • Redirection works even when the app is terminated or not in the background.
    • The notification plays the default sound, not the custom sound.
  2. Without the notification object:

    • The custom sound works fine.
    • Redirection fails when the app is terminated or not in the background (click events are not received).
  3. Including both notification and custom sound in the android section:

    • Two notifications are received:
      • One with the default sound.
      • Another with the custom sound.

Flutter Code: Here’s how I’m handling notifications in my Flutter app:

main.dart

import 'package:flutter/material.dart';
import 'package:notification_demo/fcm_controller.dart';
import 'package:notification_demo/firebase_options.dart';

Future<void> main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 await FirebaseCloudMessagingService().initFCM();
 runApp(const MyApp());
}

class MyApp extends StatefulWidget {
 const MyApp({super.key});

 @override
 State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
 @override
 Widget build(BuildContext context) {
   return const MaterialApp(
     home: Scaffold(
       body: Center(
         child: Text('hello'),
       ),
     ),
   );
 }
}

fcm_controller.dart

import 'dart:convert';
import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'dart:math' as math;
import 'package:notification_demo/firebase_options.dart';

@pragma('vm:entry-point')
Future<void> notificationTapBackground(NotificationResponse notificationResponse) async {
 debugPrint('background notification tap');
}

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
 await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
 debugPrint("Handling a background message ${message.messageId}");
 if (message.messageId != null && message.messageId!.isNotEmpty) {
   FirebaseCloudMessagingService.showNotification(message);
 }
}

class FirebaseCloudMessagingService {
 Future<void> initFCM() async {
   debugPrint('DDDD initFCM');
   try {
     await _requestPermission();
     await _initNotificationInfo();
     String deviceToken = await _getToken() ?? '';
     debugPrint('FCM Token: $deviceToken');
     await _setupMessageHandlers();
     FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
   } catch (e) {
     log("Exception: $e");
   }
 }

 Future<void> _requestPermission() async {
   NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
     alert: true,
     announcement: true,
     badge: true,
     carPlay: true,
     criticalAlert: true,
     provisional: true,
     sound: true,
   );
   debugPrint('Permission status: ${settings.authorizationStatus}');
 }

 Future<void> _initNotificationInfo() async {
   var initializationSettingAndroid = const AndroidInitializationSettings('@mipmap/ic_launcher');
   const DarwinInitializationSettings initializationSettingIOS = DarwinInitializationSettings();
   var initializationSettings = InitializationSettings(android: initializationSettingAndroid, iOS: initializationSettingIOS);
   await flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) async {
     handleNotificationTappedFormNotificationTray(jsonDecode(notificationResponse.payload ?? "{}"));
   }, onDidReceiveBackgroundNotificationResponse: notificationTapBackground);
 }

 Future<String?> _getToken() async {
   try {
     return await FirebaseMessaging.instance.getToken();
   } catch (e) {
     debugPrint("Error fetching token: $e");
     return null;
   }
 }

 Future<void> _setupMessageHandlers() async {
   FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
     showNotification(message);
   });
   FirebaseMessaging.onMessageOpenedApp.listen((event) async {
     await handleNotificationTappedFormNotificationTray(event.data);
   });
 }

 static Future<void> showNotification(RemoteMessage message) async {
   String title = message.data['title'] ?? '';
   String body = message.data['body'] ?? '';
   String soundName = 'notification_sound_android';
   String iosSoundName = 'notification_sound_android.mp3';
   if (message.data['notification_type'] == 'visitor_visited') {
     soundName = 'visitor_notification_sound';
     iosSoundName = 'visitor_notification_sound.mp3';
   }
   AndroidNotificationChannel channel = AndroidNotificationChannel(
     soundName,
     'General Notifications',
     importance: Importance.max,
     playSound: true,
     sound: RawResourceAndroidNotificationSound(soundName),
   );
   AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
     channel.id,
     channel.name,
     sound: RawResourceAndroidNotificationSound(soundName),
   );
   NotificationDetails notificationDetails = NotificationDetails(
     android: androidNotificationDetails,
     iOS: DarwinNotificationDetails(sound: iosSoundName),
   );
   flutterLocalNotificationsPlugin.show(
     math.Random().nextInt(100000),
     title,
     body,
     notificationDetails,
     payload: jsonEncode(message.data),
   );
 }

 Future<void> handleNotificationTappedFormNotificationTray(Map<String, dynamic> notificationData) async {
   debugPrint('Notification tapped: $notificationData');
   // Implement redirection logic here
 }
}

Question: How can I configure FCM and handle notifications in Flutter so that:

  1. Custom sound plays without triggering the default sound or duplicating notifications.
  2. Clicking the notification redirects to a specific screen, even if the app is terminated or not running in the background.
  3. Only one notification is displayed.

Is there a way to resolve the conflict between the notification and data objects to achieve the desired behavior?

Share Improve this question asked Jan 18 at 14:05 codnix devcodnix dev 132 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Well i do have this problems:

1.Custom sound plays without triggering the default sound or duplicating notifications.

  • Try to physical device some devices/emulator might not work. Especially on XIAOMI phone.

2.Clicking the notification redirects to a specific screen, even if the app is terminated or not running in the background.

On flutter_local_notification have a default when clicking the notification goes to the app, try playing this code: NOTE put this with initializing the _flutterLocalNotificationsPlugin1.initialize

final NotificationAppLaunchDetails? notificationAppLaunchDetails =
    await _flutterLocalNotificationsPlugin1
        .getNotificationAppLaunchDetails();
final didNotificationLaunchApp =
    notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
if (didNotificationLaunchApp == true) {
  /// GET DATA
  final dataX = notificationAppLaunchDetails?.notificationResponse!.payload;
  Future.delayed(const Duration(seconds: 2), () async {
    /// PUT CODE HERE TO REDIRECT
  });
  // throw Exception('$dataX');
}

as am using go_ router for page navigation as per my code when app is clicked or redirect or scan in qr.

  1. Only one notification is displayed.

Well on Firebase we have 2 ways to send payload with data and only the notification with body.

  • As am using is only the data. As per my condition i adjust the data to put the title and body inside so am manually fetching those. Since once on terminate i received two notification due the other notification don't have the data so that's why.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论