my IOS Flutter App shows after receiving an firebase notification a red dot (5 unread messages) on my app icon. But when you click on the notification there are still 5 unread notifications. This problem is only on IOS not on Android.
The class notificationservice contains every function for fcm.
Do someone have a clue?
main.dart:
Future<void> newApp() async {
WidgetsFlutterBinding.ensureInitialized();
await AppChecker.checkAppStatus();
if (OckData.FCMcheckExecute) {
await Firebase.initializeApp();
firebaseNotificationService fs = firebaseNotificationService();
await FirebaseMessaging.instance.subscribeToTopic('news');
await FirebaseMessaging.instance.subscribeToTopic('tester14');
await fs.firebaseMain();
}
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: true,
sound: true,
);
runApp( const MyApp());
}
notificationservice.dart:
class firebaseNotificationService {
late FirebaseMessaging messaging;
firebaseNotificationService (){
if (OckData.FCMcheckExecute) {
messaging = FirebaseMessaging.instance;
}
}
Future<String?> getToken () async{
if (OckData.FCMcheckExecute) {
return await messaging.getToken(
vapidKey: OckData.firebase_webpush,
);
}
}
listenMessage () {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
});
}
}
firebaseMain() async {
if (OckData.FCMcheckExecute) {
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
OckData.initinalRout = '/post';
if(initialMessage.notification!.title == null){
OckData.postTitle = 'FEHLER: Nicht erkannt';
OckData.postId = '38';//initialMessage.data['body'];
}
else{
OckData.postTitle = initialMessage.notification!.title.toString();
OckData.postId = initialMessage.data['body'];
}
}
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingNewHandler);
FirebaseMessaging.instance
.getInitialMessage()
.then((value) => value != null ? _firebaseMessagingNewHandler : false);
FirebaseMessaging.onMessageOpenedApp.listen(_firebaseMessagingNewHandler);
FirebaseMessaging.onMessage.listen((RemoteMessage message) async{
return await _firebaseMessagingNewHandler (message);
});
}
}
}
void postselect (String postid, String betreff){
Abos abo = Abos ();
NavKey.navigatorKey.currentState!.pushNamed('/post', arguments: [postid, betreff]).then(abo.onGoBack);
}
Future<void> _firebaseMessagingNewHandler(RemoteMessage message) async {
if (OckData.FCMcheckExecute) {
String title = '';
String id = '';
if(message.notification == null){
title = 'FEHLER: Nicht erkannt';
id = message.data['body'];
}
else{
print(message.data['body']);
title = message.notification!.title.toString();
id = message.data['body'];
}
OckData.initinalRout = '/initinalStart';
postselect(id, title);
}
}
Future<bool> checkInitinalMessage () async {
RemoteMessage? initinalMessage = await getInitialMessage();
if(initinalMessage!=null){
return true;
}
else{
return false;
}
}
Future<RemoteMessage?> getInitialMessage () async {
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
return initialMessage;
}
I solved the problem myself: The red dot is called badge. And in my Server Script there was the argument badge = 5 by sending the notification (in the curl request).
'notification' => array(
'title' => $title,
'body' => $message,
'content_available' => "true",
'sound' => 'default',
'data' => $id,
'badge' => '5'
),
Related
import 'dart:developer';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../export.dart';
class FirebaseNotificationManager {
FirebaseNotificationManager._privateConstructor();
static final FirebaseNotificationManager _instance = FirebaseNotificationManager._privateConstructor();
factory FirebaseNotificationManager() {
return _instance;
}
init() async {
// add firebase notification permission
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: true,
provisional: false,
sound: true,
);
if (await Permission.notification.request().isGranted) {
try {
// Either the permission was already granted before or the user just granted it.
FirebaseMessaging.onBackgroundMessage(_messageHandler);
_firebaseMessagingListener();
String? deviceToken = await FirebaseMessaging.instance.getToken();
logger.i(deviceToken);
} catch (e) {
logger.i(e);
}
}
}
/// must call it from view after getContext is initialized to show dialog message
checkAndroid() async {
if (!(await Permission.notification.request().isGranted) && GetPlatform.isAndroid) {
showOptionsDialog(
text: 'إذا كنت ترغب في تلقي الاشعارات ،برجاء اعطاء إذن الاشعارات في الإعدادات وإعادة تشغيل التطبيق',
yesFunction: (context) async {
openAppSettings();
});
}
}
// execute if app in background
Future<void> _messageHandler(RemoteMessage message) async {
// Data notificationMessage = Data.fromJson(message.data);
log('notification from background : ${message.toMap()}');
}
// execute if app in foreground
void _firebaseMessagingListener() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
logger.i('Got a message whilst in the foreground!');
logger.i('Message data: ${message.data}');
if (message.notification != null) {
logger.i('Message also contained a notification: ${message.notification!.toMap()}');
logger.i('Message also contained a notification: ${message.toMap()}');
// that means new message
try {
Get.snackbar(message.notification!.title.toString(), message.notification!.body.toString(),
duration: Duration(seconds: 6),
backgroundColor: Theme.of(Get.context!).cardColor,
barBlur: 10,
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(8));
} catch (e) {
logger.i(e);
}
}
});
}
}
As per the documentation, you need to put the onBackgroundMessage function outside of a class, at the top of your file as a top-level function. See Firebase messaging example for implementation.
source
file became:
import 'dart:developer';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../export.dart';
init() async {
// add firebase notification permission
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: true,
provisional: false,
sound: true,
);
if (await Permission.notification.request().isGranted) {
try {
// Either the permission was already granted before or the user just granted it.
FirebaseMessaging.onBackgroundMessage(_messageHandler);
_firebaseMessagingListener();
String? deviceToken = await FirebaseMessaging.instance.getToken();
logger.i(deviceToken);
} catch (e) {
logger.i(e);
}
}
}
/// must call it from view after getContext is initialized to show dialog message
checkAndroid() async {
if (!(await Permission.notification.request().isGranted) && GetPlatform.isAndroid) {
showOptionsDialog(
text: 'إذا كنت ترغب في تلقي الاشعارات ،برجاء اعطاء إذن الاشعارات في الإعدادات وإعادة تشغيل التطبيق',
yesFunction: (context) async {
openAppSettings();
});
}
}
// execute if app in background
Future<void> _messageHandler(RemoteMessage message) async {
// Data notificationMessage = Data.fromJson(message.data);
log('notification from background : ${message.toMap()}');
}
// execute if app in foreground
void _firebaseMessagingListener() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
logger.i('Got a message whilst in the foreground!');
logger.i('Message data: ${message.data}');
if (message.notification != null) {
logger.i('Message also contained a notification: ${message.notification!.toMap()}');
logger.i('Message also contained a notification: ${message.toMap()}');
// that means new message
try {
Get.snackbar(message.notification!.title.toString(), message.notification!.body.toString(),
duration: Duration(seconds: 6),
backgroundColor: Theme.of(Get.context!).cardColor,
barBlur: 10,
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(8));
} catch (e) {
logger.i(e);
}
}
});
}
The below is the code for notification. Can anyone say what is the mistake or is there any code to add to get the image displayed in notification tray. I am using local notifications plugin to get the notifications. The below is the code for notification. Can anyone say what is the mistake or is there any code to add to get the image displayed in notification tray. I am using local notifications plugin to get the notifications.
class LocalNotificationService {
static var userId = SharedUtils.getString('UserId');
FirebaseMessaging messaging = FirebaseMessaging.instance;
static final FlutterLocalNotificationsPlugin _notificationsPlugin =
FlutterLocalNotificationsPlugin();
static getText() {
String title;
String body;
}
static void initialize(BuildContext context) {
final InitializationSettings initializationSettings =
InitializationSettings(
android: AndroidInitializationSettings("#mipmap/ic_launcher"),
iOS: IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false));
_notificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String route) async {
String userId = SharedUtils.getString('UserId');
if (userId.isEmpty && userId == "") {
SharedUtils.setString('route', route);
Navigator.push(
context, MaterialPageRoute(builder: (context) => LoginView()));
} else {
Navigator.of(Utils.navigatorKey.currentContext)
.push(Routes.generateRoute(route));
}
});
}
static Future initializeFCM(String userId) async {
String uId = userId + "userSpecific";
FirebaseMessaging.instance.subscribeToTopic("public");
FirebaseMessaging.instance.subscribeToTopic(uId);
print(">>>>>>> fcm started $uId");
FirebaseMessaging.instance.getToken().then((value) {
print("fcm token >>> " + value);
});
}
static void display(RemoteMessage message) async {
AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
//'This channel is used for important notifications.', // description
importance: Importance.high,
playSound: true);
try {
final id = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
//channel.description,
color: Colors.blue,
playSound: true,
icon: '#mipmap/ic_launcher',
));
await _notificationsPlugin.show(
id,
message.notification.title,
message.notification.body,
notificationDetails,
payload: message.data["route"],
);
} on Exception catch (e) {
debugPrint(e.toString());
}
}
static onForeground() {
Map notificationData;
FirebaseMessaging.onMessage.listen((message) {
// LocalNotificationService.initializeFCM(userId);
if (message.notification != null) {
notificationData = {
"title": message.notification.title,
"body": message.notification.body
};
}
LocalNotificationService.display(message);
});
return notificationData;
}
static onTerminated() {
FirebaseMessaging.instance.getInitialMessage().then((message) {
if (message != null) {
final routeFromMessage = message.data["route"];
debugPrint('Route called for terminated state***************');
String userId = SharedUtils.getString('UserId');
if (userId.isEmpty && userId == "") {
SharedUtils.setString('route', routeFromMessage);
Navigator.push(Utils.navigatorKey.currentContext,
MaterialPageRoute(builder: (context) => LoginView()));
} else {
Navigator.of(Utils.navigatorKey.currentContext)
.push(Routes.generateRoute(routeFromMessage));
}
}
});
}
static onMessageOpened() {
FirebaseMessaging.onMessageOpenedApp.listen((message) {
final routeFromMessage = message.data["route"];
print('Route called for background***************');
String userId = SharedUtils.getString('UserId');
Map notificationData = {
"title": message.notification.title,
"body": message.notification.body
};
if (userId.isEmpty && userId == "") {
SharedUtils.setString('route', routeFromMessage);
Navigator.push(Utils.navigatorKey.currentContext,
MaterialPageRoute(builder: (context) => LoginView()));
} else {
Navigator.of(Utils.navigatorKey.currentContext, rootNavigator: true)
.push(Routes.generateRoute(routeFromMessage));
}
return notificationData;
});
}
}
TL;DR
I am receiving notification on all state(foreground, background(on-Paused), terminated(on-Detached)) but it's redirecting me to the intended url only in(foreground and terminated state). Surprisingly, on receiving notification during foreground state, on-select notification works on-Background state(on-Paused) as well and I am redirected to my intended url. But the main problem is while recieving notification on-background state(on-Paused) without receiving notification on foreground at first, it just redirects me to where I was. Here is the code I am currently working on:
void afterInitFirebase() async {
NotificationSettings settings = await _firebaseMessaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
await _firebaseMessaging
.subscribeToTopic(topic)
.onError((error, stackTrace) => {print(error)});
await _firebaseMessaging.getToken().then((value) => {
Preference.setString(fcm_token, value),
});
await _firebaseMessaging.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
await _firebaseMessaging.getInitialMessage().then((RemoteMessage? message) {
if (message != null) {
// _handleIncomingLinks();
MyNotification().initMessaging(message, isPush: true);
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
displayNotification(message);
});
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print("${message.from} =--> ON MESSAGE OPENED APP");
});
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
HttpOverrides.global = MyHttpOverrides();
try {
await Firebase.initializeApp().then((value) {
afterInitFirebase();
});
} catch (e) {
print(
"EXCEPTION ON MAIN:" + e.toString(),
);
}
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await fitNotification.getNotificationAppLaunchDetails();
final uri = await getInitialUri();
String initialRoute = splash_page;
String id = "0";
if (notificationAppLaunchDetails!.didNotificationLaunchApp) {
selectedNotificationPayload = notificationAppLaunchDetails.payload;
print("payload $selectedNotificationPayload");
var parts = selectedNotificationPayload?.split(SEPARATOR);
if (parts != null) {
if (parts[0] == "course" ||
parts[0].toLowerCase() == "coursedetails" ||
parts[0].toLowerCase() == "coursedetails") {
id = parts[1];
//course details page
initialRoute = course_details;
} else if (parts[0].toLowerCase() == "allcourse") {
initialRoute = all_course;
if (parts.length > 1) {
id = parts[1];
}
print("payload: $initialRoute $id");
} else if (parts[0].toLowerCase() == "allplan") {
if (parts.length > 1) {
id = parts[1];
}
initialRoute = "/allPlans";
} else if (parts[0].toLowerCase() == "web") {
id = parts[1];
initialRoute = web_page;
} else if (parts[0].toLowerCase() == "plan") {
id = parts[1];
initialRoute = plans_details_page;
} else if (parts[0].toLowerCase() == "quiz") {
initialRoute = web_page_entrance;
} else if (parts[0].toLowerCase() == "wishlist") {
initialRoute = route_wish_list;
} else if (parts[0].toLowerCase() == "carts") {
initialRoute = my_carts;
} else {
initialRoute = notification_page;
}
}
}
if (uri == null) {
} else {
String path = uri.toString();
if (path.toLowerCase().contains("coursedetails") ||
path.toLowerCase().contains("/home/course")) {
String idStr = uri.path.substring(uri.path.lastIndexOf('/') + 1);
id = idStr;
initialRoute = course_details;
}
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
displayNotification(message);
}
void displayNotification(RemoteMessage message) {
log(message.data.toString());
MyNotification().initMessaging(message);
}
This is my MyNotification class:
const String SEPARATOR = "|";
class MyNotification {
void initMessaging(RemoteMessage message, {isPush: true}) async {
var androidInit = AndroidInitializationSettings('ic_notification');
var iosInit = IOSInitializationSettings();
var initSetting =
InitializationSettings(android: androidInit, iOS: iosInit);
await fitNotification.initialize(initSetting,
onSelectNotification: onSelectNotification);
var rand = new Random();
int id = 1;
String? title = "";
String? body = "";
String? icon = "";
String? type = "";
String? itemId = "";
String link = "";
if (message.notification != null) {
title = "${message.notification?.title}";
body = "${message.notification?.body}";
icon = "${message.notification?.android?.imageUrl}";
if (Platform.isAndroid) {
icon = "${message.notification?.android?.imageUrl}";
} else {
icon = "${message.notification?.apple?.imageUrl}";
}
}
if (message.data['source'] == "webengage") {
isPush = true;
Map<String, dynamic> messageData =
jsonDecode(message.data['message_data']);
if (messageData.containsKey("title")) {
title = messageData["title"];
body = messageData["message"];
}
if (messageData.containsKey("expandableDetails")) {
Map<String, dynamic> expDetail = messageData["expandableDetails"];
if (expDetail.containsKey("image")) {
icon = expDetail["image"];
}
if (expDetail.containsKey("style")) {
if (expDetail['style'] == "RATING_V1" ||
expDetail['style'] == "CAROUSEL_V1") {
isPush = false;
}
}
}
if (messageData.containsKey("custom")) {
List<dynamic> customData = messageData['custom'];
print("element1: ${customData.toString()}");
customData.forEach((element) {
Map<String, dynamic> maps = element;
var key = maps['key'];
var value = maps['value'];
if (key == "itemId") {
itemId = value;
}
if (key == "type") {
type = value;
}
});
}
} else {
if (message.data.containsKey("icon")) {
icon = message.data['icon'];
}
if (message.data.containsKey("title")) {
title = message.data['title'];
body = message.data['body'];
}
if (message.data.containsKey("type")) {
type = message.data['type'];
}
if (message.data.containsKey("itemId")) {
itemId = message.data["itemId"];
}
}
if (title?.isNotEmpty == true && body?.isNotEmpty == true) {
showNotification(rand.nextInt(1000), title, body, icon,
"${type}$SEPARATOR${itemId}$SEPARATOR${icon}",
isPush: isPush);
}
}
Future<Uint8List> _getByteArrayFromUrl(String url) async {
final http.Response response = await http.get(Uri.parse(url));
return response.bodyBytes;
}
Future<void> showNotification(int notificationId, String? notificationTitle,
String? notificationContent, String? icon, String payload,
{String channelId = '1234',
String channelTitle = 'Android Channel',
String channelDescription = 'Default Android Channel for notifications',
Priority notificationPriority = Priority.high,
Importance notificationImportance = Importance.max,
bool isPush = true}) async {
//with icon
if (icon != null && icon.isNotEmpty) {
final String bigPicturePath =
await _downloadAndSaveFile(icon, 'bigPicture.jpg');
final BigPictureStyleInformation bigPictureStyleInformation =
BigPictureStyleInformation(
FilePathAndroidBitmap(bigPicturePath),
largeIcon: FilePathAndroidBitmap(bigPicturePath),
);
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
channelId, channelTitle,
channelDescription: channelDescription,
playSound: false,
importance: notificationImportance,
priority: notificationPriority,
styleInformation: bigPictureStyleInformation,
icon: 'for_icon',
);
final IOSNotificationDetails iOSPlatformChannelSpecifics =
IOSNotificationDetails(attachments: <IOSNotificationAttachment>[
IOSNotificationAttachment(bigPicturePath)
]);
final MacOSNotificationDetails macOSPlatformChannelSpecifics =
MacOSNotificationDetails(attachments: <MacOSNotificationAttachment>[
MacOSNotificationAttachment(bigPicturePath)
]);
final NotificationDetails notificationDetails = NotificationDetails(
iOS: iOSPlatformChannelSpecifics,
macOS: macOSPlatformChannelSpecifics,
android: androidPlatformChannelSpecifics);
if (isPush) {
await fitNotification.show(
notificationId,
notificationTitle,
notificationContent,
notificationDetails,
payload: payload,
);
}
} else {
//with out icon
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
channelId,
channelTitle,
channelDescription: channelDescription,
playSound: false,
importance: notificationImportance,
priority: notificationPriority,
icon: 'for_icon',
);
final NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
if (isPush) {
await fitNotification.show(notificationId, notificationTitle,
notificationContent, platformChannelSpecifics,
payload: payload);
}
}
var parts = payload.split(SEPARATOR);
var now = new DateTime.now();
var formatter = new DateFormat('yyyy-MM-dd HH:mm:ss');
String formattedDate = formatter.format(now);
NotificationModelData model = NotificationModelData(
courseId: parts[1],
icon: "$icon",
title: notificationTitle.toString(),
description: notificationContent.toString(),
type: parts[0],
notifyTime: formattedDate,
isRead: false,
id: now.millisecondsSinceEpoch.toString());
var db = AppDatabase.instance;
db.into(db.notificationModel).insert(model).then(
(value) => print(value),
);
}
Future<String> _downloadAndSaveFile(String url, String fileName) async {
final Directory directory = await getApplicationDocumentsDirectory();
final String filePath = '${directory.path}/$fileName';
final http.Response response = await http.get(Uri.parse(url));
final File file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
return filePath;
}
Future<dynamic> onSelectNotification(String? payload) async {
selectedNotificationPayload = payload;
var parts = payload!.split(SEPARATOR);
if (parts[0].toLowerCase() == "course") {
//course details page
Navigator.pushNamed(
navigatorKey.currentState!.overlay!.context, course_details,
arguments: <String, String>{
'course_id': parts[1],
'thumbnail': parts[2]
});
} else if (parts[0].toLowerCase() == "web") {
await Navigator.pushNamed(
navigatorKey.currentState!.overlay!.context, web_page,
arguments: <String, String>{'paymentUrl': "${parts[1]}"});
} else if (parts[0].toLowerCase() == "allcourse") {
Navigator.pushNamed(
navigatorKey.currentState!.overlay!.context, all_course,
arguments: <String, String>{'course_id': "${parts[1]}"});
} else if (parts[0].toLowerCase() == "plan") {
Navigator.pushNamed(
navigatorKey.currentState!.overlay!.context, plans_details_page,
arguments: <String, String>{'plan_id': "${parts[1]}"});
} else if (parts[0].toLowerCase() == "allplan") {
Navigator.pushNamed(
navigatorKey.currentState!.overlay!.context, all_plans,
arguments: <String, String>{'id': "${parts[1]}"});
} else if (parts[0].toLowerCase() == "quiz") {
Navigator.of(navigatorKey.currentState!.overlay!.context)
.pushNamed(web_page_entrance);
} else {
//notification page
await Navigator.pushNamed(
navigatorKey.currentState!.overlay!.context, notification_page);
}
}
This is my WebPage class:
var url = "";
class WebPage extends StatefulWidget {
void launchURL() async {
if (await canLaunch(url))
await launch(url);
else
throw "Could not launch $url";
}
#override
_WebPageState createState() => _WebPageState();
}
class _WebPageState extends State<WebPage>{
bool isLoading = true;
final _key = UniqueKey();
Map? _arguments;
var _webViewController;
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
return widget.launchURL();
});
}
#override
Widget build(BuildContext context) {
_arguments = ModalRoute.of(context)!.settings.arguments as Map?;
if (_arguments?.containsKey("paymentUrl") == true) {
url = _arguments!["paymentUrl"];
} else if (_arguments?.containsKey("course_id") == true) {
url = _arguments!["course_id"];
} else {
print(url);
}
return Scaffold(
body: SplashPage(),
);
}
}
I hope I am being clear enough. I have been stuck for days now on this particular issue with it haunting me in my dreams too. Any help would be greatly appreciated.
I'm trying to display the background and foreground notification in flutter using flutter_local_notifications package. here is my firebase class code
class FirebaseMessagingService {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
IOSInitializationSettings? _initializationSettingsIOS;
AndroidInitializationSettings? _initializationSettingsAndroid;
AndroidNotificationDetails? _androidLocalNotificationDetails;
AndroidNotificationChannel? androidNotificationchannel;
static FirebaseMessagingService? _messagingService;
NotificationDetails? _androidNotificationDetails;
InitializationSettings? _initializationSettings;
//NotificationNavigationClass _notificationNavigationClass = NotificationNavigationClass();
NotificationAppLaunchDetails? _notificationAppLaunchDetails;
bool? _didNotificationLaunchApp;
static FirebaseMessaging? _firebaseMessaging;
FirebaseMessagingService._createInstance();
factory FirebaseMessagingService() {
// factory with constructor, return some value
if (_messagingService == null) {
_messagingService = FirebaseMessagingService
._createInstance(); // This is executed only once, singleton object
_firebaseMessaging = _getMessagingService();
}
return _messagingService!;
}
static FirebaseMessaging _getMessagingService() {
return _firebaseMessaging ??= FirebaseMessaging.instance;
}
Future<String?> getToken() {
return _firebaseMessaging!.getToken();
}
Future initializeNotificationSettings() async
{
NotificationSettings? settings = await _firebaseMessaging
?.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
if (settings?.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
} else
if (settings?.authorizationStatus == AuthorizationStatus.provisional) {
print('User granted provisional permission');
} else {
print('User declined or has not accepted permission');
}
androidNotificationchannel = const AndroidNotificationChannel(
NOTIFICATION_ID, // id
NOTIFICATION_TITLE, // title
description: NOTIFICATION_DESCRIPTION,
// description
importance: Importance.max,
);
//
await _flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(androidNotificationchannel!);
if (Platform.isIOS) {
//configure local notification for ios
await _initializeIosLocalNotificationSettings();
}
else {
//configure local notification for android
await _initializeAndroidLocalNotificationSettings();
}
}
Future<void> _initializeIosLocalNotificationSettings() async
{
_initializationSettingsIOS = const IOSInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false
);
_initializationSettings = InitializationSettings(
iOS: _initializationSettingsIOS
);
}
Future<void> _initializeAndroidLocalNotificationSettings() async
{
_initializationSettingsAndroid =
const AndroidInitializationSettings('mipmap/ic_launcher');
_initializationSettings = InitializationSettings(
android: _initializationSettingsAndroid,
);
_androidLocalNotificationDetails =
const AndroidNotificationDetails(
LOCAL_NOTIFICATION_ID,
LOCAL_NOTIFICATION_TITLE,
channelDescription: LOCAL_NOTIFICATION_DESCRIPTION,
importance: Importance.max,
priority: Priority.high);
_androidNotificationDetails =
NotificationDetails(android: _androidLocalNotificationDetails);
//This will execute when the app is open and in foreground
void foregroundNotification() {
try {
_showLocalNotification(localNotificationId: DateTime
.now()
.hour + DateTime
.now()
.minute + DateTime
.now()
.second,
notificationData: message?.data,
messageTitle:message?.notification?.title ,
messageBody: message?.notification?.body,
);
} catch (error) {
log("error");
}
});
}
void _showLocalNotification(
{int? localNotificationId, Map<String, dynamic>? notificationData,String? messageTitle,String? messageBody}) async
{
if (notificationData != null) {
log("Notification Data:${notificationData}");
await _flutterLocalNotificationsPlugin.show(
localNotificationId ?? 0, notificationData["title"]??messageTitle,
notificationData["body"]??messageBody,
_androidNotificationDetails,
payload: jsonEncode(notificationData));
}
}
//This will excute when the app is in background but not killed and tap on that notification
void backgroundTapNotification() {
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage? message) async {
try {
RemoteMessage? terminatedMessage = await _firebaseMessaging
?.getInitialMessage();
} catch (error) {
log("error");
}
});
}
}
and calling it in splash screen
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
heartbeatAnimation =
Tween<double>(begin: 100.0, end: 250.0).animate(controller);
controller.forward().whenComplete(() {
controller.reverse();
});
void _setNotifications() async {
FirebaseMessagingService().foregroundNotification();
FirebaseMessagingService().backgroundTapNotification();
}
await FirebaseMessagingService().initializeNotificationSettings();
_setNotifications();
}
When i close the app and hit the API notification is displaying on background, but when i open the app and hit the API notification is not displaying, nor foreground and not on background.
please help me to get out from this problem.
I'm using awesome_notifications and flutter_background_service in conjunction to update some app state when receiving data notifications from FirebaseMessaging. As noted in the awesome_notifications, the background message handler must be a top-level function, so I am using flutter_background_service to pass data to the main isolate and update app state.
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeBackgroundService();
FirebaseMessaging.onBackgroundMessage(_backgroundMessageHandler);
_initLocalNotifications();
runApp(MyApp());
}
I'm initializing the background service similarly to the example in flutter_background_service:
Future<void> initializeBackgroundService() async {
final service = FlutterBackgroundService();
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: true,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
await service.startService();
}
and invoking update in the _backgroundMessageHandler when a notification is received:
Future<void> _backgroundMessageHandler(
RemoteMessage message,
) async {
final service = FlutterBackgroundService();
...
service.invoke('update', {
'key1': 'val1',
'key2': 'val2',
});
}
And in the StatefulWidget for my app in the main isolate, I'm listening on the update call to receive the data:
void listenForNotificationData() {
final backgroundService = FlutterBackgroundService();
backgroundService.on('update').listen((event) async {
print('received data message in feed: $event');
}, onError: (e, s) {
print('error listening for updates: $e, $s');
}, onDone: () {
print('background listen closed');
});
}
It's never invoking the listen callback on the 'update' event. I can confirm it's calling the invoke('update') portion and calling on('update').listen, but never receiving the update. It also doesn't seem to be erroring out. Am I missing a step somewhere here?
I was encountering the same issue on flutter background service. I solved it by removing the async keyword from the callback and creating a separate async function to perform the callback operations.
void listenForNotificationData() {
final backgroundService = FlutterBackgroundService();
backgroundService.on('update').listen((event) {
print('received data message in feed: $event');
}, onError: (e, s) {
print('error listening for updates: $e, $s');
}, onDone: () {
print('background listen closed');
});
}
void action(Map? event) async {
print('received data message in feed: $event');
}
Hope it helps, forgive me if there are syntax error
You can try this.
main(){
....
}
Future<void> readyForShared() async {
var sharedPreferences = await SharedPreferences.getInstance();
counterValue = sharedPreferences.getString("yourVariable") ?? "0";
}
Future<void> saveData(String value) async {
var sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString("yourVariable", value);
}
#pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
// Only available for flutter 3.0.0 and later
DartPluginRegistrant.ensureInitialized();
// For flutter prior to version 3.0.0
// We have to register the plugin manually
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString("hello", "world");
/// OPTIONAL when use custom notification
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
// bring to foreground
Timer.periodic(const Duration(seconds: 1), (timer) async {
final receivePort = ReceivePort();
// here we are passing method name and sendPort instance from ReceivePort as listener
await Isolate.spawn(computationallyExpensiveTask, receivePort.sendPort);
if (service is AndroidServiceInstance) {
if (await service.isForegroundService()) {
//It will listen for isolate function to finish
// receivePort.listen((sum) {
// flutterLocalNotificationsPlugin.show(
// 888,
// 'Title',
// 'Description ${DateTime.now()}',
// const NotificationDetails(
// android: AndroidNotificationDetails(
// 'my_foreground',
// 'MY FOREGROUND SERVICE',
// icon: 'ic_bg_service_small',
// ongoing: true,
// ),
// ),
// );
// });
var sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.reload(); // Its important
service.setForegroundNotificationInfo(
title: "My App Service",
content: "Updated at ${sharedPreferences.getString("yourVariable") ?? 'no data'}",
);
}
}
/// you can see this log in logcat
if (kDebugMode) {
// print('FLUTTER BACKGROUND SERVICE: ${deee.toString()}');
}
// test using external plugin
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
}
if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": '400',
"device": device,
},
);
});
}
....
....
....
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
readyForShared(); // init shared preferences
});
}
...
...
...
ElevatedButton(onPressed:(){saveData('Your Updated data.');}....