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

Flutter Firebase Messaging后台消息处理空值异常求助

解决FCM后台消息处理的空指针错误

问题背景

我封装了PushNotificationService单例类处理所有通知逻辑,在main函数中初始化时遇到空指针错误,错误指向FirebaseMessaging注册后台消息处理器的位置。

通知服务代码

import 'package:dio/dio.dart';
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';

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(
    RemoteMessage remoteMessage) async {
  await Firebase.initializeApp();
  await PushNotificationService.instance.initializeNotification();
  PushNotificationService.instance.showFlutterNotification(remoteMessage);
  debugPrint('Handling a background message ${remoteMessage.messageId}');
}

class PushNotificationService {
  PushNotificationService._privateConstructor();

  static final PushNotificationService instance =
      PushNotificationService._privateConstructor();
  String? fcmToken;
  FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();
  final String serverKey = "MyServerKey";
  late AndroidNotificationChannel channel;

  Future<void> initializeNotification() async {
    await Firebase.initializeApp();
    channel = const AndroidNotificationChannel(
        'high_importance_channel', // id
        'High Importance Notifications', // title
        description:
            'This channel is used for important notifications.', // description
        importance: Importance.high,
        enableVibration: true);
    await initializeLocalNotification();
    FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
    fcmToken = await firebaseMessaging.getToken();
    debugPrint('FCM Token --> $fcmToken');

    NotificationSettings notificationSettings =
        await firebaseMessaging.requestPermission(
            announcement: true, alert: true, badge: true, sound: true);

    debugPrint(
        'Notification permission status : ${notificationSettings.authorizationStatus.name}');
    if (notificationSettings.authorizationStatus ==
        AuthorizationStatus.authorized) {
      FirebaseMessaging.onMessage.listen((RemoteMessage remoteMessage) async {
        debugPrint(
            'Message title: ${remoteMessage.notification!.title}, body: ${remoteMessage.notification!.body}');
        showFlutterNotification(remoteMessage);
      });
    }
    FirebaseMessaging.onBackgroundMessage(
        (message) => _firebaseMessagingBackgroundHandler(message));
  }

  Future<void> initializeLocalNotification() async {
    AndroidInitializationSettings android =
        const AndroidInitializationSettings('@mipmap/ic_launcher');
    DarwinInitializationSettings ios = const DarwinInitializationSettings();
    InitializationSettings platform =
        InitializationSettings(android: android, iOS: ios);
    await flutterLocalNotificationsPlugin.initialize(platform);
  }

  void showFlutterNotification(RemoteMessage message) {
    RemoteNotification? notification = message.notification;
    AndroidNotification? android = message.notification?.android;
    if (notification != null && android != null && !kIsWeb) {
      flutterLocalNotificationsPlugin.show(
        notification.hashCode,
        notification.title,
        notification.body,
        NotificationDetails(
          android: AndroidNotificationDetails(channel.id, channel.name,
              channelDescription: channel.description,
              icon: '@mipmap/ic_launcher',
              priority: Priority.high,
              importance: Importance.high),
        ),
      );
    }
  }

  Future<void> sendNotification(String fcmToken, String msg) async {
    Dio dio = Dio();
    dio.post(
      'https://fcm.googleapis.com/fcm/send',
      options: Options(
        headers: {
          "Content-Type": "application/json",
          "Authorization": "key=$serverKey"
        },
      ),
      data: {
        "to": fcmToken,
        "notification": {
          "title": "AppName",
          "body": msg,
        },
      },
    );
  }
}

main函数初始化代码

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await MobileAds.instance.initialize();

  //push notification service
  await PushNotificationService.instance.initializeNotification();

  runApp(
    EasyDynamicThemeWidget(
      child: const MyApp(),
    ),
  );
}

错误日志

E/flutter (13038): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Null check operator used on a null value
E/flutter (13038): #0      MethodChannelFirebaseMessaging.registerBackgroundMessageHandler (package:firebase_messaging_platform_interface/src/method_channel/method_channel_messaging.dart:201:53)
E/flutter (13038): #1      FirebaseMessagingPlatform.onBackgroundMessage= (package:firebase_messaging_platform_interface/src/platform_interface/platform_interface_messaging.dart:107:16)
E/flutter (13038): #2      FirebaseMessaging.onBackgroundMessage (package:firebase_messaging/src/messaging.dart:73:31)
E/flutter (13038): #3      PushNotificationService.initializeNotification (package:app_user/services/push_notification_service.dart:56:25)
E/flutter (13038): <asynchronous suspension>
E/flutter (13038): #4      main (package:app_user/main.dart:28:3)
E/flutter (13038): <asynchronous suspension>

环境信息

  • Flutter版本:3.10.6(stable通道)
  • 系统:Windows 10 19045.3324
  • Android SDK版本:30.0.3
  • 使用的依赖包版本:
    • firebase_auth: ^4.7.3
    • firebase_core: ^2.15.1
    • firebase_messaging: ^14.6.7
    • flutter_local_notifications: ^15.1.1

解决方案

问题根源

  1. 后台消息处理器_firebaseMessagingBackgroundHandler中重复调用initializeNotification(),导致重复注册FirebaseMessaging.onBackgroundMessage,引发空指针。
  2. main函数中已经初始化过Firebase.initializeApp(),通知服务类中再次初始化属于冗余操作。
  3. 后台消息处理器中不需要初始化整个通知服务,只需初始化本地通知插件即可。

修改步骤

  1. 调整后台消息处理器:移除重复的通知服务初始化,仅初始化本地通知并显示消息:
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage remoteMessage) async {
  await Firebase.initializeApp();
  // 仅初始化本地通知,不要调用整个initializeNotification
  final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  const AndroidInitializationSettings android = AndroidInitializationSettings('@mipmap/ic_launcher');
  const DarwinInitializationSettings ios = DarwinInitializationSettings();
  const InitializationSettings platform = InitializationSettings(android: android, iOS: ios);
  await flutterLocalNotificationsPlugin.initialize(platform);
  
  // 创建临时通知通道(后台环境无法访问类成员)
  const channel = AndroidNotificationChannel(
      'high_importance_channel', 
      'High Importance Notifications',
      description: 'This channel is used for important notifications.',
      importance: Importance.high,
      enableVibration: true);
  
  RemoteNotification? notification = remoteMessage.notification;
  AndroidNotification? androidNotif = remoteMessage.notification?.android;
  if (notification != null && androidNotif != null && !kIsWeb) {
    flutterLocalNotificationsPlugin.show(
      notification.hashCode,
      notification.title,
      notification.body,
      NotificationDetails(
        android: AndroidNotificationDetails(channel.id, channel.name,
            channelDescription: channel.description,
            icon: '@mipmap/ic_launcher',
            priority: Priority.high,
            importance: Importance.high),
      ),
    );
  }
  debugPrint('Handling a background message ${remoteMessage.messageId}');
}
  1. 修改通知服务初始化方法:
    • 移除重复的Firebase.initializeApp()调用;
    • 将FirebaseMessaging.onBackgroundMessage的注册移到方法最外层,避免权限判断导致的注册遗漏;
    • 确保通道创建后调用flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);来创建通知通道。

修改后的initializeNotification方法:

Future<void> initializeNotification() async {
  channel = const AndroidNotificationChannel(
      'high_importance_channel', // id
      'High Importance Notifications', // title
      description:
          'This channel is used for important notifications.', // description
      importance: Importance.high,
      enableVibration: true);
  
  // 创建通知通道
  await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);
  
  await initializeLocalNotification();
  FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
  fcmToken = await firebaseMessaging.getToken();
  debugPrint('FCM Token --> $fcmToken');

  NotificationSettings notificationSettings =
      await firebaseMessaging.requestPermission(
          announcement: true, alert: true, badge: true, sound: true);

  debugPrint(
      'Notification permission status : ${notificationSettings.authorizationStatus.name}');
  
  FirebaseMessaging.onMessage.listen((RemoteMessage remoteMessage) async {
    debugPrint(
        'Message title: ${remoteMessage.notification!.title}, body: ${remoteMessage.notification!.body}');
    showFlutterNotification(remoteMessage);
  });
  
  // 移到权限判断外,确保一定会注册后台处理器
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
  1. main函数保持不变,但确保Firebase.initializeApp()只调用一次。

额外注意事项

  • 后台消息处理器必须是顶级函数,不能是类成员或匿名函数,你当前的写法符合要求;
  • 后台环境中无法访问单例类的成员变量,所以需要在处理器内部重新初始化必要组件;
  • 确保AndroidManifest.xml中配置了正确的FCM相关权限和服务。

内容的提问来源于stack exchange,提问作者asd1001

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 22:17:03