`I'm developing a Flutter app that utilizes Firebase push notifications, and I'm relatively new to mobile development.
I'm facing challenges with implementing a feature where, upon receiving a message, the app should display a popup modal with the message text. The popup modal successfully displays when the app is in the foreground.
However, I encounter two issues:
When I set up background messaging and use FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);, I encounter errors during app execution. The specific error is _TypeError (Null check operator used on a null value).
Additionally, I would like the app to receive messages when it's in the background or the screen is locked. When the user unlocks the screen and navigates to the app, I want the popup modal to display the received data from the notification.
I guess you may not have been able to update the updated notification classes. So I'll show you the API I use for notifications and let you know where you should call it. Here is the class I use for notification;
import 'dart:developer';
import 'package:app_settings/app_settings.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:hive/hive.dart';
import '../app_utils/common.dart';
class PushNotificationsManager {
PushNotificationsManager._();
factory PushNotificationsManager() => _instance;
static final PushNotificationsManager _instance =
PushNotificationsManager._();
//initialising firebase message plugin
late final FirebaseMessaging _messaging = FirebaseMessaging.instance;
late final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
AndroidNotificationChannel createNotificationChannel() {
return const AndroidNotificationChannel(
'High Importance Channel',
'High Importance Notifications',
importance: Importance.high,
);
}
AndroidNotificationDetails createNotificationDetails(
AndroidNotificationChannel channel) {
return AndroidNotificationDetails(
channel.id.toString(),
channel.name.toString(),
channelDescription: channel.description.toString(),
importance: Importance.high,
groupKey: channel.groupId,
priority: Priority.high,
);
}
// function to request notifications permissions
void requestNotificationPermission() async {
NotificationSettings settings = await _messaging.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: true,
criticalAlert: true,
provisional: true,
sound: true);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
debugPrint('user granted permission');
} else if (settings.authorizationStatus ==
AuthorizationStatus.provisional) {
debugPrint('user granted provisional permission');
} else {
AppSettings.openAppSettings(type: AppSettingsType.notification);
debugPrint('user denied permission');
}
}
//function to initialise flutter local notification plugin to show notifications for android when app is active
void initLocalNotifications(
BuildContext context, RemoteMessage message) async {
var androidInitializationSettings =
const AndroidInitializationSettings('@mipmap/launcher_icon');
var iosInitializationSettings = const DarwinInitializationSettings();
var initializationSetting = InitializationSettings(
android: androidInitializationSettings, iOS: iosInitializationSettings);
await _flutterLocalNotificationsPlugin.initialize(
initializationSetting,
onDidReceiveNotificationResponse: (payload) {
handleMessage(message);
},
);
}
void firebaseInit(BuildContext context) {
FirebaseMessaging.onMessage.listen((message) {
initLocalNotifications(context, message);
showNotification(message);
});
}
// function to show visible notification when app is active
Future<void> showNotification(RemoteMessage message) async {
const DarwinNotificationDetails darwinNotificationDetails =
DarwinNotificationDetails(
presentAlert: true, presentBadge: true, presentSound: true);
NotificationDetails notificationDetailsPlatformSpecific =
NotificationDetails(
android: createNotificationDetails(createNotificationChannel()),
iOS: darwinNotificationDetails);
showDefaultNotification(message.notification!.title ?? "",
message.notification!.body!, notificationDetailsPlatformSpecific);
}
void showDefaultNotification(String title, String body,
NotificationDetails notificationDetailsPlatformSpecific) {
Future.delayed(Duration.zero, () async {
await _flutterLocalNotificationsPlugin.show(
0,
title.toString(),
body,
notificationDetailsPlatformSpecific,
);
});
}
//function to get device token on which we will send the notifications
Future<String> getDeviceToken() async {
String? deviceId = await _messaging.getToken();
Common.deviceToken.value = deviceId!;
return deviceId;
}
void isTokenRefresh() async {
_messaging.onTokenRefresh.listen((event) {
event.toString();
log('refresh');
});
}
//handle tap on notification when app is in background or terminated
Future<void> setupInteractMessage(context) async {
// when app is terminated
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
handleMessage(initialMessage);
}
//when app ins background
FirebaseMessaging.onMessageOpenedApp.listen((event) {
handleMessage(event);
});
FirebaseMessaging.onBackgroundMessage(_handleBackgroundMessage);
}
Future<void> _handleBackgroundMessage(RemoteMessage message) async {
handleMessage(message);
}
void handleMessage(RemoteMessage message) {
if (message.notification!.body!.contains("")) {}
}
}
Now You can use it in MyApp class
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
PushNotificationsManager().requestNotificationPermission();
PushNotificationsManager().firebaseInit(context);
PushNotificationsManager().setupInteractMessage(context);
//notificationServices.isTokenRefresh();
PushNotificationsManager().getDeviceToken().then((value) {
debugPrint('device token');
debugPrint(value);
});
super.initState();
}