使用FirebaseMessaging.instance.getInitialMessage()处理终止状态时收到nul



我正在使用Flutter开发一个应用程序,并使用带有firebase_messaging: ^10.0.4Flutter插件的FCM实现推送通知:

当移动应用程序处于终止状态时,我正在使用Firebase在移动应用程序上发送通知。为了使通知处于终止状态,我使用FirebaseMessaging.instance.getInitialMessage()来处理通知的点击。当用户点击通知时,他们将被路由到一个特定的屏幕(显示传递的消息(。

问题是,我在移动应用程序中收到了处于终止状态的通知,但当我点击通知时,我没有被路由到我从Firebase传入的特定屏幕,并且消息中的FirebaseMessaging.instance.getInitialMessage()值为null。

如果有人知道这件事,请告诉我。

main.dart

checkFirebase() async {
await Firebase.initializeApp();
// 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.max,
);
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.showFrontNotification
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
}
}


class _ProcessAppState extends State<ProcessApp> {
Future<void> _initializeFuture;
Future<void> _initializeServices() async {
await Firebase.initializeApp();
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
var dir = await getApplicationDocumentsDirectory();
Hive.init(dir.path);
// pass all uncaught errors to crashlytics
Function originalOnError = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails errorDetails) async {
await FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
originalOnError(errorDetails);
};
// 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.max,
);
/*
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,
);
}
}
@override
void initState() {
super.initState();
_initializeFuture = _initializeServices();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: FutureBuilder(
future: _initializeFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
reportError(
error: snapshot.error, stackTrace: snapshot.stackTrace);
return Center(
child: Text(context.translateText(key: "general_error")),
);
}
if (snapshot.connectionState == ConnectionState.done) {
return MyApp();
}
return progressBar();
},
),
),
);
}
}

@override
Widget build(BuildContext context) {

return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: gradientTopColor,
statusBarBrightness: Brightness.dark,
),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: primaryColor,
backgroundColor: whiteColor,
fontFamily: 'Roboto'),
supportedLocales: [
Locale('en'),
Locale('ta'),
Locale('ml'),
Locale('kn'),
Locale('te'),
],
localizationsDelegates: [
// for our own localizations
AppLocalizations.delegate,
// localizations for all material widgets provided
GlobalMaterialLocalizations.delegate,
// localizations for all cupertino widgets provided
DefaultCupertinoLocalizations.delegate,
// for rtl, ltr text directions
GlobalWidgetsLocalizations.delegate,
],
locale: _locale,
localeResolutionCallback: (deviceLocale, supportedLocales) {
try {
return Locale(defaultLang);
} catch (e) {
print(e);
return Locale("en");
}
},
// navigation analytics reporting
// navigatorObservers: <NavigatorObserver>[observer],
home: NotificationMessageHandler(child: LauncherScreen()),

builder: EasyLoading.init(),
),
);
}

message_handler.dart

class _NotificationMessageHandlerState extends State<NotificationMessageHandler>
with AfterLayoutMixin<NotificationMessageHandler> {
@override
void initState() {
super.initState();
// _checkForUpdate();
var initializationSettingsAndroid =
AndroidInitializationSettings("@mipmap/ic_launcher");
var initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (payload) async {
log("payload : $payload", name: "onSelectNotification");
handleNotificationClick(context, jsonDecode(payload));
});
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage initialMessage) async {
log("message : $initialMessage", name: "getInitialMessage");
handleNotificationClick(context, jsonDecode(initialMessage.toString()));
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print(message);
showFrontNotification(message);
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
handleNotificationClick(context, message.data);
});

}

@override
Widget build(BuildContext context) {
return widget.child;
}
}

当您的应用程序终止并且您想要导航到另一个屏幕时,您需要MaterialApp的navigatorKey中的上下文,以及您实际想要去的关键字,关键字we将"点击动作在FCM请求正文中键入。

我建议您在一个单独的文件中处理您的Firebase消息代码。

fcm_service.dart此文件包含前台、后台和应用程序终止后的路线导航器,还处理带有图像的通知。

import 'dart:convert';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart' as http;
import '../main.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'custom_notification_channel_id',
'Notification',
description: 'notifications from Your App Name.',
importance: Importance.high,
);
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
}
void setupFcm() {
var initializationSettingsAndroid = const AndroidInitializationSettings('@mipmap/ic_launcher');
var initializationSettingsIOs = const IOSInitializationSettings();
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOs,
);
//when the app is in foreground state and you click on notification.
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String payload) {
if (payload != null) {
Map<String, dynamic> data = json.decode(payload);
goToNextScreen(data);
}
});
//When the app is terminated, i.e., app is neither in foreground or background.
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage message) {
//Its compulsory to check if RemoteMessage instance is null or not.
if (message != null) {
goToNextScreen(message.data);
}
});
//When the app is in the background, but not terminated.
FirebaseMessaging.onMessageOpenedApp.listen((event) {
goToNextScreen(event.data);
},
cancelOnError: false,
onDone: () {},
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
if (android.imageUrl != null && android.imageUrl.trim().isNotEmpty) {
final String largeIcon = await _base64encodedImage(
android.imageUrl,
);
final BigPictureStyleInformation bigPictureStyleInformation =
BigPictureStyleInformation(
ByteArrayAndroidBitmap.fromBase64String(largeIcon),
largeIcon: ByteArrayAndroidBitmap.fromBase64String(largeIcon),
contentTitle: notification.title,
htmlFormatContentTitle: true,
summaryText: notification.body,
htmlFormatSummaryText: true,
hideExpandedLargeIcon: true,
);
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: 'custom_notification_icon',
color: primaryColor,
importance: Importance.max,
priority: Priority.high,
largeIcon: ByteArrayAndroidBitmap.fromBase64String(largeIcon),
styleInformation: bigPictureStyleInformation,
),
),
payload: json.encode(message.data),
);
}
else {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: 'custom_notification_icon',
color: primaryColor,
importance: Importance.max,
priority: Priority.high,
),
),
payload: json.encode(message.data),
);
}
}
});
}
Future<void> deleteFcmToken() async {
return await FirebaseMessaging.instance.deleteToken();
}
Future<String> getFcmToken() async {
String token = await FirebaseMessaging.instance.getToken();
return Future.value(token);
}
void goToNextScreen(Map<String, dynamic> data) {
if (data['click_action'] != null) {
switch (data['click_action']) {
case "first_screen":
navigatorKey.currentState.pushNamed(FirstScreen.routeName,);
break;
case "second_screen":
navigatorKey.currentState.pushNamed(SecondScreen.routeName,);
break;
case "sample_screen":
navigatorKey.currentState.pushNamed(SampleScreen.routeName,);
}
return;
}
//If the payload is empty or no click_action key found then go to Notification Screen if your app has one.
navigatorKey.currentState.pushNamed(NotificationPage.routeName,);
}
Future<String> _base64encodedImage(String url) async {
final http.Response response = await http.get(Uri.parse(url));
final String base64Data = base64Encode(response.bodyBytes);
return base64Data;
}

main.dart

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
runApp(const MyApp());
}
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);

@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
setupFcm();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
onGenerateRoute: //Define your named routes.
);
}
}

此外,您需要定义默认通知通道id,以及可选的默认通知图标、默认通知颜色

AndroidManifest.xml

<application>
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="custom_notification_channel_id" />
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/custom_notification_icon" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message.  -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/notification_icon_color" />
</application>

您还可以查看示例FCM HTTP请求以处理">";数据">键JsonObject和";点击动作在里面。

URL: https://fcm.googleapis.com/fcm/send
Request Method: POST
Header: {
"Authorization": "key={value1}",
"Sender": "id={value2}",
}
Request Body: {
"registration_ids": [
"exwlH32_S0il4ky4ZRXCrg:APA91bHp4kL-IJmtHRGFcQlhUauEY1ZiqZFfWsDkWqsB-yHDUzRVx63e8ehSirUTbSg6NqMqAAfcW16tk4dgs-NtTcCVShipGt9JWIJK_r8b4ldqFYGhzZcNF0VTiVKWzWkRQQIncCoE"
],
"notification": {
"title": "Wear Mask",
"body": "Maintain social distance",
"image": "https://repository-images.githubusercontent.com/31792824/fb7e5700-6ccc-11e9-83fe-f602e1e1a9f1",
"imageUrl": "https://repository-images.githubusercontent.com/31792824/fb7e5700-6ccc-11e9-83fe-f602e1e1a9f1",
"sound": "default"
},
"data": {
"click_action": "sample_screen",
"custom_key": "custom_value",
"image": "https://repository-images.githubusercontent.com/31792824/fb7e5700-6ccc-11e9-83fe-f602e1e1a9f1",
"imageUrl": "https://repository-images.githubusercontent.com/31792824/fb7e5700-6ccc-11e9-83fe-f602e1e1a9f1"
}
}

注意:registration_ids键在列表中只取1000个值。

在">"中;数据">JsonObject,您可以定义自定义键值对,这将派上用场。例如,你想打开一个特定的屏幕,比如event_screen.dart,你需要通过事件id从服务器获取事件的详细信息;数据"相应的对象

"data": {
"click_action": "event_screen",
"event_id": "23"
}

我知道我参加聚会迟到了,但希望这对将来的人有所帮助。我也面临着同样的问题,我使用了Smeet Bhatt的上述答案,但我仍然面临同样的问题。我注意到我点击得太快可能是await Firebase.initializeApp();还没有完成。当我等待一段时间时,RemoteMessageFirebaseMessaging.instance.getInitialMessage()上从未为空。

最新更新