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

Flutter中firebase_messaging ^5.1.5报toRawHandle空方法调用错误求助

Firebase Messaging 5.1.5: NoSuchMethodError when handling background notifications

我在Flutter应用中使用firebase_messaging: ^5.1.5实现Firebase推送通知,需要支持后台及终止状态下接收推送,但严格按照官方文档配置后,仍然触发以下错误:

Unhandled Exception: NoSuchMethodError: The method 'toRawHandle' was called on null

我的代码实现

notification_handler.dart

import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;

class NotificationHandler {
  FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
  FirebaseMessaging _fcm = FirebaseMessaging();
  StreamSubscription iosSubscription;

  static final NotificationHandler _singleton = new NotificationHandler._internal();

  factory NotificationHandler() {
    return _singleton;
  }

  NotificationHandler._internal();

  Future<dynamic> myBackgroundMessageHandler(
      Map<String, dynamic> message) async {
    print("onLaunch: $message");
    _showBigPictureNotification(message);
    // Or do other work.
  }

  initializeFcmNotification() async {
    flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
    var initializationSettingsAndroid = new AndroidInitializationSettings('ic_launcher');
    var initializationSettingsIOS = new IOSInitializationSettings(
        onDidReceiveLocalNotification: onDidReceiveLocalNotification);
    var initializationSettings = new InitializationSettings(
        initializationSettingsAndroid, initializationSettingsIOS);
    flutterLocalNotificationsPlugin.initialize(initializationSettings, onSelectNotification: onSelectNotification);

    if (Platform.isIOS) {
      iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
        // save the token OR subscribe to a topic here
      });
      _fcm.requestNotificationPermissions(IosNotificationSettings());
    } else {
      _saveDeviceToken();
    }

    _fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        _showBigPictureNotification(message);
      },
      onBackgroundMessage: myBackgroundMessageHandler,
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
      },
    );
  }

  /// Get the token, save it to the database for current user
  _saveDeviceToken() async {
    String fcmToken = await _fcm.getToken();
    print("FCM_TOKEN: $fcmToken");
  }

  Future<void> _showBigPictureNotification(message) async {
    var rng = new Random();
    var notifId = rng.nextInt(100);
    var largeIconPath = await _downloadAndSaveImage(
        'https://cdn.pixabay.com/photo/2019/04/21/21/29/pattern-4145023_960_720.jpg',
        'largeIcon');
    var bigPicturePath = await _downloadAndSaveImage(
        'https://cdn.pixabay.com/photo/2019/04/21/21/29/pattern-4145023_960_720.jpg',
        'bigPicture');
    var bigPictureStyleInformation = BigPictureStyleInformation(
        bigPicturePath,
        BitmapSource.FilePath,
        largeIcon: largeIconPath,
        largeIconBitmapSource: BitmapSource.FilePath,
        contentTitle: message['data']['title'],
        htmlFormatContentTitle: true,
        summaryText: message['data']['body'],
        htmlFormatSummaryText: true);
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        '12', 'trading_id', message['data']['body'],
        importance: Importance.High,
        priority: Priority.High,
        style: AndroidNotificationStyle.BigPicture,
        styleInformation: bigPictureStyleInformation);
    var platformChannelSpecifics =
        NotificationDetails(androidPlatformChannelSpecifics, null);
    await flutterLocalNotificationsPlugin.show(
        notifId, message['data']['title'], message['data']['body'],
        platformChannelSpecifics, payload: message['data']['body']);
  }

  Future<void> _showBigTextNotification(message) async {
    var rng = new Random();
    var notifId = rng.nextInt(100);
    var bigTextStyleInformation = BigTextStyleInformation(
        message['data']['body'],
        htmlFormatBigText: true,
        contentTitle: message['data']['title'],
        htmlFormatContentTitle: true,
        summaryText: message['data']['body'],
        htmlFormatSummaryText: true);
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        '12', 'trading_id', '',
        importance: Importance.High,
        priority: Priority.High,
        style: AndroidNotificationStyle.BigText,
        styleInformation: bigTextStyleInformation);
    var platformChannelSpecifics =
        NotificationDetails(androidPlatformChannelSpecifics, null);
    await flutterLocalNotificationsPlugin.show(
        notifId, message['data']['title'], message['data']['body'],
        platformChannelSpecifics, payload: message['data']['body']);
  }

  Future onSelectNotification(String payload) async {
    if (payload != null) {
      debugPrint('notification payload: ' + payload);
    }
    // await Navigator.push(
    // context,
    // new MaterialPageRoute(builder: (context) => new SecondScreen(payload)),
    // );
  }

  Future<void> onDidReceiveLocalNotification(
      int id, String title, String body, String payload) async {
    // display a dialog with the notification details, tap ok to go to another page
  }

  Future<String> _downloadAndSaveImage(String url, String fileName) async {
    var directory = await getApplicationDocumentsDirectory();
    var filePath = '${directory.path}/$fileName';
    var response = await http.get(url);
    var file = File(filePath);
    await file.writeAsBytes(response.bodyBytes);
    return filePath;
  }
}

首页initState调用

@override
void initState() {
  // TODO: implement initState
  super.initState();
  new NotificationHandler().initializeFcmNotification();
}

问题原因与解决方案

核心原因

这个错误的根源在于后台消息处理函数myBackgroundMessageHandler运行在独立的Isolate中,无法访问Flutter主Isolate中初始化的flutterLocalNotificationsPlugin实例——也就是说,当后台消息触发时,flutterLocalNotificationsPlugin是null,调用它的show方法就会抛出toRawHandle was called on null的异常。

Flutter的后台消息Isolate是一个纯Dart环境,没有初始化Flutter的UI框架和插件实例,所以任何依赖Flutter引擎的操作(比如展示本地通知)都会失败。

具体解决方案

  1. 重构后台消息处理逻辑
    移除myBackgroundMessageHandler中对_showBigPictureNotification的调用,改为仅处理数据逻辑(比如日志记录、本地存储更新):

    Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
      print("onBackgroundMessage: $message");
      // 这里只处理数据相关的逻辑,比如保存消息到本地数据库
      // 不要调用flutterLocalNotificationsPlugin的任何方法
      return Future.value(true);
    }
    
  2. 依赖Firebase原生通知展示(后台/终止状态)
    如果需要在后台或终止状态下展示通知,确保你的推送消息包含notification字段(而非纯data消息)。Firebase SDK会自动在系统层面展示这条通知,无需你手动调用本地通知插件:
    示例推送消息结构:

    {
      "notification": {
        "title": "你的通知标题",
        "body": "你的通知内容"
      },
      "data": {
        "custom_key": "custom_value"
      },
      "to": "设备的FCM令牌"
    }
    

    当应用处于后台/终止状态时,Firebase会直接触发系统通知,用户点击后会启动应用并触发onLaunch或onResume回调。

  3. 确保原生配置正确

    • Android:检查AndroidManifest.xml是否包含Firebase消息服务配置:
      <service
          android:name="io.flutter.plugins.firebasemessaging.FirebaseMessagingService"
          android:exported="false">
          <intent-filter>
              <action android:name="com.google.firebase.MESSAGING_EVENT"/>
          </intent-filter>
      </service>
      
    • iOS:确认Info.plist中开启了远程通知权限,并且已正确配置APNs证书:
      <key>UIBackgroundModes</key>
      <array>
          <string>remote-notification</string>
      </array>
      
  4. 自定义后台通知样式(可选)
    如果需要在后台展示自定义样式的通知(比如大图),在firebase_messaging: ^5.1.5版本中,你需要通过原生端扩展实现:

    • Android:在原生FirebaseMessagingService的onMessageReceived方法中构建自定义样式通知
    • iOS:在AppDelegate中处理远程通知并构建自定义通知

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:44:52