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

Flutter应用前台接收FCM通知时触发空指针异常求助

解决Flutter FCM前台通知NullPointerException问题

问题现象

应用处于后台或被杀死状态时,FCM通知可正常显示;但应用处于前台时,会抛出以下NullPointerException:

E/flutter (26732): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference, null, java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference
E/flutter (26732):  at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.setSmallIcon(FlutterLocalNotificationsPlugin.java:425)
E/flutter (26732):  at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.createNotification(FlutterLocalNotificationsPlugin.java:326)
E/flutter (26732):  at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.showNotification(FlutterLocalNotificationsPlugin.java:1146)
E/flutter (26732):  at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.show(FlutterLocalNotificationsPlugin.java:1462)
E/flutter (26732):  at com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin.onMethodCall(FlutterLocalNotificationsPlugin.java:1299)
E/flutter (26732):  at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
E/flutter (26732):  at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)

经调试,问题出在_configureForegroundMessaging()方法的通知显示代码中。

相关代码

主实现代码

late SharedPreferences _sharedPreferences;
bool isLoggedIn = false;
Future<void> _initializePrefs() async {
  log("Loading Shared Preferences");
  _sharedPreferences = await SharedPreferences.getInstance();
  log('Shared Preferences loaded');
  isLoggedIn = _sharedPreferences.getBool(IS_LOGGED_IN) ?? false;
}

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

late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;

Future<void> setupFlutterNotifications() async {
  if (isFlutterLocalNotificationsInitialized) {
    return;
  }
  channel = const AndroidNotificationChannel(
    'high_importance_channel', // id
    'High Importance Notifications', // title
    // 'This channel is used for important notifications.', // description
    importance: Importance.high,
  );

  flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);

  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );
  isFlutterLocalNotificationsInitialized = true;
}

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,
          icon: 'launch_background',
        ),
      ),
    );
  }
}

void _configureForegroundMessaging() {
  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    log('new notificaiton: ${message.data}');
    RemoteNotification? notification = message.notification;
    AndroidNotification? android = message.notification?.android!;

    if (notification != null && android != null) {
      log('Notification hascode: ${notification.hashCode} and notification title: ${notification.title} and notification body: ${notification.body}');
      flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              // channel.description,
              icon: 'launch_background',
            ),
          ));
    }
  });
}

late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  if (!kIsWeb) {
    await setupFlutterNotifications();
  }
  _configureForegroundMessaging();

  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);
  await _initializePrefs();
  runApp(const MyApp());
}

Android Manifest配置

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.blog_app">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application
        android:label="blog_app"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            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">
            <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>
        </activity>
       
        <service
            android:name="com.google.firebase.messaging.FirebaseMessagingService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="high_importance_channel" />
    </application>
</manifest>

解决方案

1. 修复通知图标问题

错误栈指向setSmallIcon方法,说明插件无法找到指定的图标资源。将icon: 'launch_background'替换为应用已存在的图标名称,比如启动图标ic_launcher(确保@mipmap/ic_launcher资源存在于Android项目中)。

2. 完善AndroidNotificationDetails参数

在AndroidNotificationDetails中添加channelDescription参数,部分插件版本要求该参数必填:

AndroidNotificationDetails(
  channel.id,
  channel.name,
  channelDescription: channel.description, // 补充描述
  icon: 'ic_launcher',
)

同时在setupFlutterNotifications中为channel添加description:

channel = const AndroidNotificationChannel(
  'high_importance_channel',
  'High Importance Notifications',
  description: 'This channel is used for important notifications.',
  importance: Importance.high,
);

3. 修复空安全强制解包问题

_configureForegroundMessaging中AndroidNotification? android = message.notification?.android!;使用了强制解包!,若message.notification为null会直接崩溃,改为安全访问:

AndroidNotification? android = message.notification?.android;

4. 确保通知通道初始化完成

在显示通知前增加通道初始化状态检查,避免未初始化时调用:

if (notification != null && android != null && isFlutterLocalNotificationsInitialized) {
  // 显示通知逻辑
}

修改后的_configureForegroundMessaging方法

void _configureForegroundMessaging() {
  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    log('new notification: ${message.data}');
    RemoteNotification? notification = message.notification;
    AndroidNotification? android = message.notification?.android;

    if (notification != null && android != null && isFlutterLocalNotificationsInitialized) {
      log('Notification hascode: ${notification.hashCode} and notification title: ${notification.title} and notification body: ${notification.body}');
      flutterLocalNotificationsPlugin.show(
        notification.hashCode,
        notification.title,
        notification.body,
        NotificationDetails(
          android: AndroidNotificationDetails(
            channel.id,
            channel.name,
            channelDescription: channel.description,
            icon: 'ic_launcher',
          ),
        ),
      );
    }
  });
}

原因分析

  • 图标资源不存在:指定的launch_background不是有效的drawable/mipmap资源,插件无法获取其资源ID,导致null值触发NPE。
  • 空安全漏洞:强制解包操作!会在对象为null时直接崩溃,违反空安全规范。
  • 参数缺失:部分版本的flutter_local_notifications插件要求AndroidNotificationDetails必须包含channelDescription,缺失会导致底层参数为空引发错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 12:58:13