Flutter集成FirebaseMessaging锁屏无通知及后台角标实现求助
解决Flutter FCM锁屏不显示通知及后台角标问题
一、锁屏显示通知解决方案
问题根源
你当前仅在收到消息时触发业务逻辑(弹对话框、播放音频),但锁屏/后台状态下,纯数据类型的FCM消息不会自动触发系统通知,必须通过本地通知插件主动构建并推送系统级通知,才能在锁屏界面展示。
实现步骤
- 添加依赖
在pubspec.yaml中引入本地通知插件:
dependencies: flutter_local_notifications: ^16.1.0 # 版本以官方最新为准
- 修改
PushNotificationSystem类,集成本地通知服务
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class PushNotificationSystem { FirebaseMessaging messaging = FirebaseMessaging.instance; late FlutterLocalNotificationsPlugin _localNotificationsPlugin; Future initializeCloudMessaging(BuildContext context) async { // 初始化本地通知 await _initLocalNotifications(); // 1. 应用终止状态 FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? remoteMessage) { if (remoteMessage != null) { // 清除角标 await FlutterAppBadger.removeBadge(); readUserRideRequestInformation(remoteMessage.data["rideRequestId"], context); } }); // 2. 应用前台状态 FirebaseMessaging.onMessage.listen((RemoteMessage remoteMessage) { if (remoteMessage != null) { // 显示本地通知(锁屏可见) _showLocalNotification(remoteMessage); // 更新角标 await FlutterAppBadger.updateBadgeCount(1); readUserRideRequestInformation(remoteMessage.data["rideRequestId"], context); } }); // 3. 应用后台状态 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage remoteMessage) { if (remoteMessage != null) { // 清除角标 await FlutterAppBadger.removeBadge(); readUserRideRequestInformation(remoteMessage.data["rideRequestId"], context); } }); // 后台消息处理(隔离环境,不能直接用context) FirebaseMessaging.onBackgroundMessage(_backgroundMessageHandler); } // 初始化本地通知配置 Future _initLocalNotifications() async { _localNotificationsPlugin = FlutterLocalNotificationsPlugin(); // Android初始化配置 const AndroidInitializationSettings androidInit = AndroidInitializationSettings('@mipmap/ic_launcher'); // iOS初始化配置 const IOSInitializationSettings iosInit = IOSInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); const InitializationSettings initSettings = InitializationSettings( android: androidInit, iOS: iosInit, ); await _localNotificationsPlugin.initialize(initSettings); // Android必须创建通知渠道,否则锁屏不显示通知 const AndroidNotificationChannel channel = AndroidNotificationChannel( 'ride_request_channel', '打车请求通知', description: '接收用户打车请求的通知渠道', importance: Importance.max, enableVibration: true, playSound: true, lockscreenVisibility: NotificationVisibility.public, // 锁屏显示完整内容 ); await _localNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel); } // 构建并显示本地通知 Future _showLocalNotification(RemoteMessage message) async { const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'ride_request_channel', '打车请求通知', channelDescription: '新的用户打车请求', importance: Importance.max, priority: Priority.high, sound: RawResourceAndroidNotificationSound('notification_sound'), // 自定义音频需放在android/app/src/main/res/raw目录 lockscreenVisibility: NotificationVisibility.public, ); const IOSNotificationDetails iosDetails = IOSNotificationDetails( sound: 'notificationSound.mp3', badgeNumber: 1, ); const NotificationDetails notificationDetails = NotificationDetails( android: androidDetails, iOS: iosDetails, ); await _localNotificationsPlugin.show( DateTime.now().millisecondsSinceEpoch ~/ 1000, '新打车请求', '来自${message.data["userName"]}的打车请求', notificationDetails, payload: message.data["rideRequestId"], ); } // 后台消息处理函数(必须是静态/顶级函数,隔离环境无context) static Future<void> _backgroundMessageHandler(RemoteMessage message) async { await Firebase.initializeApp(); // 后台环境重新初始化本地通知 FlutterLocalNotificationsPlugin localNotifications = FlutterLocalNotificationsPlugin(); const AndroidInitializationSettings androidInit = AndroidInitializationSettings('@mipmap/ic_launcher'); const IOSInitializationSettings iosInit = IOSInitializationSettings(); await localNotifications.initialize(const InitializationSettings(android: androidInit, iOS: iosInit)); // 后台创建通知渠道 const AndroidNotificationChannel channel = AndroidNotificationChannel( 'ride_request_channel', '打车请求通知', description: '新的用户打车请求', importance: Importance.max, lockscreenVisibility: NotificationVisibility.public, ); await localNotifications.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel); // 后台显示通知 await localNotifications.show( DateTime.now().millisecondsSinceEpoch ~/ 1000, '新打车请求', '来自${message.data["userName"]}的打车请求', const NotificationDetails( android: AndroidNotificationDetails( 'ride_request_channel', '打车请求通知', channelDescription: '新的用户打车请求', importance: Importance.max, priority: Priority.high, lockscreenVisibility: NotificationVisibility.public, ), iOS: IOSNotificationDetails(badgeNumber: 1), ), payload: message.data["rideRequestId"], ); // 后台更新角标 await FlutterAppBadger.updateBadgeCount(1); } // 原readUserRideRequestInformation方法保持不变(注意:后台不能调用showDialog) readUserRideRequestInformation(String userRideRequestId, BuildContext context) { // ... 你的原有代码 } // 原generateAndGetToken方法保持不变 Future generateAndGetToken() async { // ... 你的原有代码 } }
关键注意事项
- Android必须配置通知渠道,并设置
lockscreenVisibility: NotificationVisibility.public,否则锁屏时通知会被隐藏。 - 后台消息处理函数不能直接使用
context,只能通过本地通知触应用户交互,用户点击通知后再进入应用处理业务逻辑。 - 自定义通知音频:Android需将音频文件放在
android/app/src/main/res/raw目录,iOS需添加到项目资源并配置权限。
二、后台角标实现解决方案
实现步骤
- 添加依赖
在pubspec.yaml中引入角标插件:
dependencies: flutter_app_badger: ^1.5.0 # 版本以官方最新为准
- 角标操作逻辑
- 收到通知时:调用
FlutterAppBadger.updateBadgeCount(1)(支持累加数量,比如updateBadgeCount(currentCount + 1)) - 用户点击通知进入应用时:调用
FlutterAppBadger.removeBadge()清除角标 - 后台消息处理中:在
_backgroundMessageHandler内调用FlutterAppBadger.updateBadgeCount(1)更新角标
平台配置
- iOS:在
Info.plist中添加以下配置,开启后台通知和角标权限:
<key>UIBackgroundModes</key> <array> <string>remote-notification</string> </array> <key>UIUserNotificationSettings</key> <dict> <key>UIUserNotificationTypeBadge</key> <true/> </dict>
- Android:部分品牌(小米、华为等)需要用户手动开启“桌面图标角标”权限,可在应用设置中引导用户开启。
内容的提问来源于stack exchange,提问作者Simone
相关产品推荐
相关产品推荐

