Flutter中firebase_messaging ^5.1.5报toRawHandle空方法调用错误求助
我在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引擎的操作(比如展示本地通知)都会失败。
具体解决方案
重构后台消息处理逻辑
移除myBackgroundMessageHandler中对_showBigPictureNotification的调用,改为仅处理数据逻辑(比如日志记录、本地存储更新):Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async { print("onBackgroundMessage: $message"); // 这里只处理数据相关的逻辑,比如保存消息到本地数据库 // 不要调用flutterLocalNotificationsPlugin的任何方法 return Future.value(true); }依赖Firebase原生通知展示(后台/终止状态)
如果需要在后台或终止状态下展示通知,确保你的推送消息包含notification字段(而非纯data消息)。Firebase SDK会自动在系统层面展示这条通知,无需你手动调用本地通知插件:
示例推送消息结构:{ "notification": { "title": "你的通知标题", "body": "你的通知内容" }, "data": { "custom_key": "custom_value" }, "to": "设备的FCM令牌" }当应用处于后台/终止状态时,Firebase会直接触发系统通知,用户点击后会启动应用并触发
onLaunch或onResume回调。确保原生配置正确
- 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>
- Android:检查
自定义后台通知样式(可选)
如果需要在后台展示自定义样式的通知(比如大图),在firebase_messaging: ^5.1.5版本中,你需要通过原生端扩展实现:- Android:在原生
FirebaseMessagingService的onMessageReceived方法中构建自定义样式通知 - iOS:在
AppDelegate中处理远程通知并构建自定义通知
- Android:在原生
内容的提问来源于stack exchange,提问作者Harish Penta

