You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成FirebaseMessaging锁屏无通知及后台角标实现求助

解决Flutter FCM锁屏不显示通知及后台角标问题

一、锁屏显示通知解决方案

问题根源

你当前仅在收到消息时触发业务逻辑(弹对话框、播放音频),但锁屏/后台状态下,纯数据类型的FCM消息不会自动触发系统通知,必须通过本地通知插件主动构建并推送系统级通知,才能在锁屏界面展示。

实现步骤

  1. 添加依赖
    在pubspec.yaml中引入本地通知插件:
dependencies:
  flutter_local_notifications: ^16.1.0 # 版本以官方最新为准
  1. 修改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需添加到项目资源并配置权限。

二、后台角标实现解决方案

实现步骤

  1. 添加依赖
    在pubspec.yaml中引入角标插件:
dependencies:
  flutter_app_badger: ^1.5.0 # 版本以官方最新为准
  1. 角标操作逻辑
  • 收到通知时:调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 12:35:31