Flutter - Endless notification on a single pusher beams event - flutter

Describe the bug
I am using pusher beams to fire event from server and I use flutter local notification to show the notification when the event is received by app.
Sample code to reproduce the problem
I have called initPusherBeams() in my init state (please read to the end I am quite sure this issues is with flutter local notifications)
#override
void initState() {
super.initState();
_setAuthData().then((_) {
if (_user?.id != null) initPusherBeams();
});
// notification related
_notiInit();
_requestPermissions();
_configureDidReceiveLocalNotificationSubject();
_configureSelectNotificationSubject();
// ask for app rating
WidgetsBinding.instance.addPostFrameCallback((_) => _ratingDialog());
}
and then, ininitPusherBeams function, I have
initPusherBeams() async {
// Let's see our current interests
await PusherBeams.instance.setDeviceInterests([
// 'App.Models.User.${_user!.id}',
'debug-new'
]);
// This is not intented to use in web
if (!kIsWeb) {
await PusherBeams.instance.onMessageReceivedInTheForeground(_onMessageReceivedInTheForeground);
}
}
void _onMessageReceivedInTheForeground(Map<Object?, Object?> data) {
AndroidNotificationDetails androidPlatformChannelSpecifics = const AndroidNotificationDetails(
'channel',
'My App Name',
channelDescription: 'New user registered',
playSound: false,
styleInformation: DefaultStyleInformation(true, true),
);
const IOSNotificationDetails iOSPlatformChannelSpecifics = IOSNotificationDetails(presentSound: false);
NotificationDetails platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics,
);
log(json.encode(data));
// flutterLocalNotificationsPlugin.show(
// 0,
// 'New user registered',
// data['body'].toString(),
// platformChannelSpecifics,
// payload: data['title'].toString(),
// );
}
If I comment out flutterLocalNotificationsPlugin.show, the event fire only once as you can see in below screenshot.
but if I uncomment showing notification part which is the following code
flutterLocalNotificationsPlugin.show(
0,
'New user registered',
data['body'].toString(),
platformChannelSpecifics,
payload: data['title'].toString(),
);
The event fire endlessly (like in the screenshot below) and the notification keep appearing for each event continuously.
How come showing notification became some kind of loop and how should I fix this. Thanks in advance.

I end up showing the notification with SnackBar while the app is in foreground. While the apps is in background, the pusher beams package handle the notification and send it to notification center. Here's my code
initPusherBeams() async {
// Let's see our current interests
await PusherBeams.instance.setDeviceInterests([
'App.Models.User.${_user!.id}',
'debug-new'
]);
// This is not intented to use in web
if (!kIsWeb) {
await PusherBeams.instance.onMessageReceivedInTheForeground(_onMessageReceivedInTheForeground);
}
}
void _onMessageReceivedInTheForeground(Map<Object?, Object?> data) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(correctFont(data['body'] as String)),
duration: const Duration(milliseconds: 3000),
backgroundColor: brandBrown,
),
);
}```

Related

Flutter RepeatInterval local notification

anybody knows how to make the periodicaly show in flutter to show different notification and not the same over and over again?
Here is my code:
Future<void> showNotification(int id, String title, String body) async {
await flutterLocalNotificationsPlugin.periodicallyShow(
id,
title,
body,
RepeatInterval.everyMinute,
const NotificationDetails(
// Android details
android: AndroidNotificationDetails('main_channel', 'Main Channel',
channelDescription: "ashwin",
importance: Importance.max,
priority: Priority.max),
// iOS details
iOS: DarwinNotificationDetails(
sound: 'default.wav',
presentAlert: true,
presentBadge: true,
presentSound: true,
),
),
androidAllowWhileIdle:
true, // To show notification even when the app is closed
);
}
It works fine but it show me the same notification over and over again, I call this function in another class and in the body I have a randomName variable from a list, the problem it s not updating at every notification as i want and it display one random String again at every interval of time. I want to be updated every time the user get a notification.
String? randomName;
final random = new Random();
randomName = names[random.nextInt(names.length)];
onPressed: () {
setState(() {
showToast();
NotificationService().showNotification(
1,
'sada',
randomName!,
);
});
},
This 2 codes are from another class

Flutter local notifications

I' m using Flutter local notification for my app with repeatInterval, it's working fine but i want notifications to be different from one to another, cause with this code i get the same string from list over and over again. How can i edit the code to get another string when user get a notification?
Future<void> showNotification(int id, String title, String body) async {
await flutterLocalNotificationsPlugin.periodicallyShow(
id,
title,
body,
RepeatInterval
.everyMinute,
const NotificationDetails(
// Android details
android: AndroidNotificationDetails('main_channel', 'Main Channel',
channelDescription: "ashwin",
importance: Importance.max,
priority: Priority.max),
// iOS details
iOS: DarwinNotificationDetails(
sound: 'default.wav',
presentAlert: true,
presentBadge: true,
presentSound: true,
),
),
androidAllowWhileIdle:
true, // To show notification even when the app is closed
);
}
Here is the code from another class NotificationService and i called it here:
onPressed: () {
setState(() {
showToast();
NotificationService().showNotification(
1,
'$randomNames${widget.userPost}',
randomName!,
);
});
},
The problem it s only updating when i press the button again, i want to be updated every time when the user get a notification. Anybody know how to do that?
String? randomName;
final random = new Random();
randomName = names[random.nextInt(names.length)];
This is where i randomized the list called names, this code and the one from above are from a diferent screen class called IntroPage
How can I solve this problem?
if you want to set different notif, which means you need the the system to execute the function. but since its repeated notif, and the message was static, consider to use Workmanger.
https://pub.dev/packages/workmanager
with this ,you can execute your function to generate random text as a notification.
#pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) {
//you function here to generete random String
// then show local notificaton
showNotification(); // call local notificaiton
return Future.value(true);
});
}
void main() {
Workmanager().initialize(
callbackDispatcher, // The top level function, aka callbackDispatcher
isInDebugMode: true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
);
Workmanager().registerPeriodicTask("task-identifier", "simpleTask");
runApp(MyApp());
}

iOS firebase getInitialMessage not working Flutter

I installed firebase notification package in my Flutter project and setup the necessary settings for sending data.
Everything works fine on android, when the app is completely closed, when I send a parameter via firebase, the page I want opens, but on the ios part, when I click on the notification, it does not catch the click and the page I want does not open.
I would be very happy if you could help me where is the main point I should pay attention to for the ios part.
Main.dart
initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
if (initialMessage!.data['type'] == 'notification') {
await Future.delayed(const Duration(seconds: 4));
navigatorKey.currentState?.pushNamed(
'/notification'); // navigate to login, with null-aware check
}
}
pushNotificationService.dart
class PushNotificationService {
Future<void> setupInteractedMessage() async {
await Firebase.initializeApp();
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null &&
initialMessage.data['type'] == 'notification') {
await Future.delayed(const Duration(seconds: 6));
navigatorKey.currentState?.pushNamed(
'/notification'); // navigate to login, with null-aware check
}
// Also handle any interaction when the app is in the background via a
// Stream listener
// This function is called when the app is in the background and user clicks on the notification
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
// Get.toNamed(NOTIFICATIOINS_ROUTE);
print(message.toMap().toString());
print(message.data['type'].toString());
if (message.data['type'] == 'notification') {
navigatorKey.currentState?.pushNamed('/notification');
}
});
await enableIOSNotifications();
await registerNotificationListeners();
}
registerNotificationListeners() async {
AndroidNotificationChannel channel = androidNotificationChannel();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
var androidSettings =
const AndroidInitializationSettings('#mipmap/ic_launcher');
var iOSSettings = const IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
);
var initSetttings =
InitializationSettings(android: androidSettings, iOS: iOSSettings);
flutterLocalNotificationsPlugin.initialize(initSetttings,
onSelectNotification: (message) async {
// This function handles the click in the notification when the app is in foreground
// Get.toNamed(NOTIFICATIOINS_ROUTE);
});
// onMessage is called when the app is in foreground and a notification is received
FirebaseMessaging.onMessage.listen((RemoteMessage? message) {
// Get.find<HomeController>().getNotificationsNumber();
print(message!.data['type'].toString());
print(message.toMap().toString());
if (message.data['type'] == 'notification') {
navigatorKey.currentState?.pushNamed('/notification');
}
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
// If `onMessage` is triggered with a notification, construct our own
// local notification to show to users using the created channel.
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
icon: android.smallIcon,
playSound: true,
),
),
);
}
});
}
enableIOSNotifications() async {
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true, // Required to display a heads up notification
badge: true,
sound: true,
);
}
androidNotificationChannel() => const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
importance: Importance.max,
);
}
info.plist background modules
UIBackgroundModes
fetch
processing
remote-notification

FirebaseMessaging.instance.getInitialMessage() is not called in terminated state

FirebaseMessaging.instance.getInitialMessage() is not called in terminated state when we click notification that contains message data like this ,instead it is displaying screen from splash screen
{
"registration_ids": [
""
],
"data": {
"title": "Flutter9 Object Notification.3...",
"body": "this is flutter Data Object notification test message from",
"android_channel_id": "dgdgsdfgs",
},
"android":{
"priority":"high"
}
}
Given that you not displayed how you setup your fcm , here is how to go about it
/// Create a [AndroidNotificationChannel] for heads up notifications
late AndroidNotificationChannel channel;
/// Initialize the [FlutterLocalNotificationsPlugin] package.
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
/// To verify things are working, check out the native platform logs.
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
print('Handling a background message ${message.messageId}');
}
in main.dart
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
// setup displaying notifications
channel = const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title// description
importance: Importance.high,
);
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
/// Create an Android Notification Channel.
///
/// We use this channel in the `AndroidManifest.xml` file to override the
/// default FCM channel to enable heads up notifications.
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
handle message call this in your initstate
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) {
if (message != null) {
// do message things
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
showMessage(message: message);
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
),
iOS: IOSNotificationDetails(subtitle: notification.title)));
}
});
incase you need to navigate on receiving a message
void showMessage({required RemoteMessage message}) async {
if (message.data['route'] == "mypage") {
var myparams = message.data['payload'];
Navigator.of(context).push(MaterialPageRoute(
fullscreenDialog: true,
builder: (_) {
return MyPage(
params:myparams,
);
})); ...
With FCM, you can send two types of messages to clients:
Notification messages, are sometimes thought of as "display messages." These are handled by the FCM SDK automatically.
Data messages, which are handled by the client app. "
https://firebase.google.com/docs/cloud-messaging/concept-options
What you are trying to send is a data message, my best guess is that the firebase SDK does not recognize the data message so it does not trigger the firebase message event listener. Your notification must have a 'notification' field for firebase SDK to listen to.

flutter - don't show notification when user is on certain page

My app has chatting function. And I have implemented Firebase Cloud Messaging and Local Notification.
So when someone sends me a message, notification(one inside android status bar) and local notification(one on the upper part of the screen that holds for like 3 seconds) is shown. They show up when app is opened, on background, and also when app is terminated. (So far so good)
But I don't want both notifications(1. fcm notification, 2. local notification) to show up when I'm already in the chat page with that person. And I have no idea where to start.
Below is my code for handling fcm and show local notification. They are inside main.dart and the main function calls initializeFCM before returning my root page.
Future<void> initializeFCM() async {
await Firebase.initializeApp();
FirebaseMessaging messaging = FirebaseMessaging.instance;
// Firebase Messaging
NotificationSettings settings = await messaging.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');
}
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage = await messaging.getInitialMessage();
if (initialMessage != null) {
handleMessage(initialMessage);
}
// initialize local notification
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
// terminated app을 local notification 눌러서 열었을 때 - payload가 정상적으로 동작하지 않음
final NotificationAppLaunchDetails? notificationAppLaunchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
final didNotificationLaunchApp = notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
if (didNotificationLaunchApp) {
onSelectNotification(notificationAppLaunchDetails!.payload);
}
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
showLocalNotification(message);
});
FirebaseMessaging.onBackgroundMessage(showLocalNotification);
FirebaseMessaging.onMessageOpenedApp.listen(handleMessage);
}
void handleMessage(RemoteMessage message) {
if (message.data["screen"] == "message") {
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => MessageListPage(),
)
);
}
}
Future<void> onSelectNotification(payload) async {
if(payload != null) {
Map<String, dynamic> data = json.decode(payload);
if (data['screen'] == "message") {
Navigator.push(
navigatorKey.currentContext!,
MaterialPageRoute(
builder: (context) => MessageListPage(),
)
);
}
}
}
Future<void> showLocalNotification(RemoteMessage message) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
// String screen = message.data['screen'];
// iOS heads up notification setting
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true, // Required to display a heads up notification
badge: true,
sound: true,
);
// android channel setting
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
importance: Importance.max,
);
// initialize local notification
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin.initialize(InitializationSettings(
android: AndroidInitializationSettings('#drawable/mentea_ic_stat_name'),
iOS: IOSInitializationSettings()),
onSelectNotification: onSelectNotification);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
// If `onMessage` is triggered with a notification, construct our own
// local notification to show to users using the created channel.
if (notification != null && android != null) {
// print('Message also contained a notification: ${message.notification}');
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
'high_importance_channel', //channel.id,
'High Importance Notifications', //channel.name,
icon: android.smallIcon,
color: primaryColor,
// other properties...
),
),
payload: json.encode(message.data)
);
}
}
I don't know if you still have this problem, but I'll post my solution here.
It happens that when we send a push notification with the notification message feature, the FCM plugin automatically sends a notification on the device if the App is in background and finished, where we can only customize the foreground notification through Local Notifications or other solutions within of the App. So the solution pointed out in the Firebase documentation for this situation is to send the push notification only as a data message, without including the notification object. Below I show the examples and the documentation link.
Documentation: https://firebase.google.com/docs/cloud-messaging/concept-options?authuser=1&hl=pt#notifications_and_data_messages
Combined notification example:
const payload = {
notification: {
title: '$FooCorp up 1.43% on the day',
body: '$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
},
data: {
stock: 'GOOG',
open: '829.62',
close: '635.67'
}
};
Notification example with data message only:
const payload = {
data: {
score: '850',
time: '2:45'
}
};
Notification example with notification message only:
const payload = {
notification: {
title: '$FooCorp up 1.43% on the day',
body: '$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
}
};
So, so that you can customize all forms of notification (foreground, background and terminated), send a push notification through your backend as a data message only.