我在 Flutter 应用程序中使用 Firebase Cloud Messaging (FCM),并且 试图实现以下目标:
收到通知时播放自定义通知声音。 单击通知时重定向到特定屏幕,甚至 如果应用程序已终止或未在后台运行。
问题:
自定义声音问题:
重定向问题:
我正在使用的有效负载:
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"
}
}
}
观察结果:
随着通知对象:
没有通知对象:
包括通知和自定义声音android部分:
颤振代码: 以下是我在 Flutter 应用程序中处理通知的方式:
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
}
}
问题: 如何在 Flutter 中配置 FCM 并处理通知,以便:
有没有办法解决 notification 和 data 对象之间的冲突以实现所需的行为?
我确实有这个问题:
1.自定义声音播放,不会触发默认声音或重复通知。
2.单击通知会重定向到特定屏幕,即使应用程序已终止或未在后台运行。
flutter_local_notification 有一个默认的点击通知进入应用程序时,尝试播放以下代码:
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');
}
正如我使用 go_ 单击应用程序或重定向或扫描 qr 时,根据我的代码进行页面导航的路由器。
在 Firebase 上,我们有 2 种方法发送带有数据的有效负载,并且仅发送带有正文的通知。