我正在致力于在 Flutter 中使用 FCM 和 flutter_local_notifications 实现推送通知。我目前只在 Android 上进行测试。通知在前台工作正常,但是当应用程序在后台时,我只收到一次通知,但它在通知栏中显示重复。
这是我的 AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:label="flutter_weather"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:showWhenLocked="true"
android:turnScreenOn="true"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data
android:name="firebase_messaging_auto_init_enabled"
android:value="false" />
</application>
</manifest>
这是我的
main.dart
:
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_dotenv/flutter_dotenv.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_weather/firebase_options.dart';
import 'package:flutter_weather/screens/notification_screen.dart';
import 'package:flutter_weather/screens/test_screen.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class WeatherApp extends StatefulWidget {
const WeatherApp({super.key});
@override
createState() => _WeatherAppState();
}
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await setupFlutterNotifications(); // setting method
showFlutterNotification(message); // local notification
}
// FCM foreground processing - Show local notification
void showFlutterNotification(RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
0,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: '',
),
),
payload: message.data["screen"],
);
}
}
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;
// setting method
Future<void> setupFlutterNotifications() async {
if (isFlutterLocalNotificationsInitialized) {
return;
}
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();
await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings(
android: AndroidInitializationSettings("@mipmap/ic_launcher"),
iOS: DarwinInitializationSettings(),
),
onDidReceiveNotificationResponse: onTabNotification,
onDidReceiveBackgroundNotificationResponse: onTabNotification,
);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
await FirebaseMessaging.instance.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
getToken();
isFlutterLocalNotificationsInitialized = true;
}
// Function to navigate when notification is clicked
void onTabNotification(NotificationResponse details) async {
if (details.payload != null) {
print('onDidReceiveNotificationResponse - payload: ${details.payload}');
navigatorKey.currentState?.pushNamed("/${details.payload}");
}
}
Future<void> getToken() async {
// ios
String? token;
if (defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
token = await FirebaseMessaging.instance.getAPNSToken();
}
// aos
else {
token = await FirebaseMessaging.instance.getToken();
}
print("Token: $token");
}
void handleMessage(RemoteMessage? message) {
// navigate to new screen when message is received and user tabs notification
if (message != null) {
navigatorKey.currentState?.pushNamed(
"/${message.data["screen"]}",
arguments: message,
);
}
}
void main() async {
await dotenv.load(fileName: ".env");
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupFlutterNotifications();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(const WeatherApp());
}
class _WeatherAppState extends State<WeatherApp> {
@override
void initState() {
super.initState();
FirebaseMessaging.onMessage.listen(showFlutterNotification);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessageOpenedApp.listen(handleMessage);
FirebaseMessaging.instance.getInitialMessage().then(handleMessage);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "title",
initialRoute: "/",
routes: {
"/notification_screen": (context) => const NotificationPage(),
},
navigatorKey: navigatorKey,
home: const TestScreen(),
);
}
}
这是我的 JSON:
{
"notification": {"title": "background ",
"body" : "background test 123123", "alert":"true},
"priority": "high",
"data": {
"type": "notification",
"click_action": "FLUTTER_NOTIFICATION_CLICK", "screen": "notification_screen",
"id": "1"},
"to": "MY_DEVICE_TOKEN"
}
这个问题似乎与我如何处理
_firebaseMessagingBackgroundHandler
中的后台消息以及setupFlutterNotifications
中通知渠道的设置有关。我尝试了各种解决方案,但问题仍然存在。
我遇到了删除“通知”字段的建议,所以我尝试了,但没有成功。
我想是因为这条线
showFlutterNotification(message);
这意味着您将显示该消息两次。一次 FCM,再次通过本地通知