I have been trying to add a scheduled notification feature to my todo app but am unable to do so, when the notification is supposed to appear, it does not and the app crashes, I want the notification to be added when the add button in the todoScreen is clicked.
any help would be really appreciated.
todoScreen link: https://github.com/Rohith-JN/Reminders_App/blob/main/lib/Screens/TodoScreen.dart
notification link: https://github.com/Rohith-JN/Reminders_App/blob/main/lib/notification_service.dart
Okay pushing notification with flutter is pretty easy.
Add these dependencies to pubspec.yaml:
dependencies:
flutter_local_notifications: ^1.4.2
rxdart: ^0.23.1
Particular plugins: flutter_local_notifications and rxdart
then run this command in the terminal:
flutter pub get
Go to AndroidManifest.xml in /android/app/src/main/ and add these lines:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
Now, go to AppDelegate.swift in /ios/Runner/ Add:
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as ?
UNUserNotificationCenterDelegate
}
before these lines:
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
Done?
Now, Create a notifications_helper.dart file and import the flutter_local_notifications and rxdart packages.
Then in that file add these:
class NotificationClass{
final int id;
final String title;
final String body;
final String payload;
NotificationClass({this.id, this.body, this.payload, this.title});
}
Also add these finals:
final rxSub.BehaviorSubject<NotificationClass> didReceiveLocalNotificationSubject =
rxSub.BehaviorSubject<NotificationClass>();
final rxSub.BehaviorSubject<String> selectNotificationSubject =
rxSub.BehaviorSubject<String>();
Now finally add the following method to helper file:
Future<void> initNotifications(
notifs.FlutterLocalNotificationsPlugin
notifsPlugin) async {
var initializationSettingsAndroid =
notifs.AndroidInitializationSettings('icon');
var initializationSettingsIOS = notifs.IOSInitializationSettings(
requestAlertPermission: false,
requestBadgePermission: false,
requestSoundPermission: false,
onDidReceiveLocalNotification:
(int id, String title, String body, String payload) async {
didReceiveLocalNotificationSubject
.add(NotificationClass(id: id, title: title, body: body, payload: payload));
});
var initializationSettings = notifs.InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
await notifsPlugin.initialize(initializationSettings,
onSelectNotification: (String payload) async {
if (payload != null) {
print('notification payload: ' + payload);
}
selectNotificationSubject.add(payload);
});
print("Notifications initialised successfully");
}
For permission request method for iOS:
void requestIOSPermissions(
notifs.FlutterLocalNotificationsPlugin notifsPlugin) {
notifsPlugin.resolvePlatformSpecificImplementation<notifs.IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
Done Up To This?
Now, scheduled notification, add these:
Future<void> scheduleNotification(
{notifs.FlutterLocalNotificationsPlugin notifsPlugin,
String id,
String title,
String body,
DateTime scheduledTime}) async {
var androidSpecifics = notifs.AndroidNotificationDetails(
id, // This specifies the ID of the Notification
'Scheduled notification', // This specifies the name of the notification channel
'A scheduled notification', //This specifies the description of the channel
icon: 'icon',
);
var iOSSpecifics = notifs.IOSNotificationDetails();
var platformChannelSpecifics = notifs.NotificationDetails(
androidSpecifics, iOSSpecifics);
await notifsPlugin.schedule(0, title, "Scheduled notification",
scheduledTime, platformChannelSpecifics); // This literally schedules the notification
}
Now modify the main.dart file:
NotificationAppLaunchDetails notifLaunch;
final FlutterLocalNotificationsPlugin notifsPlugin=
FlutterLocalNotificationsPlugin();
Now within the main method, add
notifLaunch = await notifsPlugin.getNotificationAppLaunchDetails();
await initNotifications(notifsPlugin);
requestIOSPermissions(notifsPlugin);
NOW MAIN THING,
Triggering a scheduled Notification, Import your helper file and main.dart:
import '../helpers/notifications_helper.dart';
import '../main.dart';
Now call the scheduleNotification method:
scheduleNotification(
notifsPlugin: notifsPlugin, //Or whatever you've named it in main.dart
id: DateTime.now().toString(),
body: "A scheduled Notification",
scheduledTime: DateTime.now()); //Or whenever you actually want to trigger it
And My friend you are done!👍
Related
I'm simply adding a local notification, however in real device notification is appears but without making a sound. For certain real devices, the notification is enabled by default, but we have to manually enable it in some devices. can anyone know how should i add permission in this code
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
class NotificationService {
final FlutterLocalNotificationsPlugin notificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> initNotification() async {
AndroidInitializationSettings initializationSettingsAndroid =
const AndroidInitializationSettings('mipmap/ic_launcher');
var initializationSettingsIOS = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
onDidReceiveLocalNotification:
(int id, String? title, String? body, String? payload) async {});
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
await notificationsPlugin.initialize(initializationSettings,
onDidReceiveNotificationResponse:
(NotificationResponse notificationResponse) async {});
}
notificationDetails() {
return const NotificationDetails(
android: AndroidNotificationDetails('channelId', 'channelName',
importance: Importance.max,
playSound: true,
),
iOS: DarwinNotificationDetails());
}
Future showNotification(
{int id = 0, String? title, String? body, String? payLoad}) async {
return notificationsPlugin.show(
id, title, body, await notificationDetails());
}
Future scheduleNotification(
{int id = 0,
String? title,
String? body,
String? payLoad,
required DateTime scheduledNotificationDateTime}) async {
return notificationsPlugin.zonedSchedule(
id,
title,
body,
tz.TZDateTime.from(
scheduledNotificationDateTime,
tz.local,
),
await notificationDetails(),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
}
}
You can use permission_handler plugin to request permission on iOS, android and windows.
For Example:
PermissionStatus status = await Permission.notification.request();
if (status.isGranted) {
// notification permission is granted
}
else {
// Open settings to enable notification permission
}
if you are using for iOS then don't forget to add this in you pod file
post_install do |installer|
installer.pods_project.targets.each do |target|
... # Here are some configurations automatically generated by flutter
# Start of the permission_handler configuration
target.build_configurations.each do |config|
# You can enable the permissions needed here. For example to enable camera
# permission, just remove the `#` character in front so it looks like this:
#
# ## dart: PermissionGroup.camera
# 'PERMISSION_CAMERA=1'
#
# Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=1',
]
end
# End of the permission_handler configuration
end
end
I've been trying to get Flutter local notifications to work since a week but can't get it to work.
Basically the issue is whenever i create a daily notification, it works only for the first time and then it doesn't show notifications every next day.
Suppose if i set daily scheduled notification at 12:20 PM, it will show notification at 12:20 PM for first time, then the next day it won't show. And when i see the list of pending notifications i can see the notification still present.
here's all my notification code
class NotificationService {
// Singleton pattern
static final NotificationService _notificationService =
NotificationService._internal();
factory NotificationService() {
return _notificationService;
}
NotificationService._internal();
static const channelId = "1";
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
static const AndroidNotificationDetails _androidNotificationDetails =
AndroidNotificationDetails(
channelId,
"thecodexhub",
channelDescription:
"This channel is responsible for all the local notifications",
playSound: true,
priority: Priority.high,
importance: Importance.high,
);
static const IOSNotificationDetails _iOSNotificationDetails =
IOSNotificationDetails();
final NotificationDetails notificationDetails = const NotificationDetails(
android: _androidNotificationDetails,
iOS: _iOSNotificationDetails,
);
Future<void> init() async {
const AndroidInitializationSettings androidInitializationSettings =
AndroidInitializationSettings('#mipmap/ic_launcher');
const IOSInitializationSettings iOSInitializationSettings =
IOSInitializationSettings(
defaultPresentAlert: false,
defaultPresentBadge: false,
defaultPresentSound: false,
);
const InitializationSettings initializationSettings =
InitializationSettings(
android: androidInitializationSettings,
iOS: iOSInitializationSettings,
);
// *** Initialize timezone here ***
tz.initializeTimeZones();
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: onSelectNotification,
);
}
Future<void> requestIOSPermissions() async {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
Future<void> showNotification(
int id, String title, String body, String payload) async {
await flutterLocalNotificationsPlugin.show(
id,
title,
body,
notificationDetails,
payload: payload,
);
}
Future<void> scheduleNotification(int id, String title, String body,
DateTime eventDate, TimeOfDay eventTime, String payload,
[DateTimeComponents? dateTimeComponents]) async {
final scheduledTime = eventDate.add(Duration(
hours: eventTime.hour,
minutes: eventTime.minute,
));
await flutterLocalNotificationsPlugin.zonedSchedule(
id,
title,
body,
tz.TZDateTime.from(scheduledTime, tz.local),
notificationDetails,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
androidAllowWhileIdle: true,
payload: payload,
matchDateTimeComponents: dateTimeComponents,
);
}
Future<void> cancelNotification(int id) async {
await flutterLocalNotificationsPlugin.cancel(id);
}
Future<void> cancelAllNotifications() async {
await flutterLocalNotificationsPlugin.cancelAll();
}
Future getNotifications() async {
final List<PendingNotificationRequest> pendingNotificationRequests =
await FlutterLocalNotificationsPlugin().pendingNotificationRequests();
return pendingNotificationRequests;
}
}
Future<void> onSelectNotification(String? payload) async {
// await navigatorKey.currentState
// ?.push(MaterialPageRoute(builder: (_) => DetailsPage(payload: payload)));
}
and here's how i'm calling it.
await notificationService.scheduleNotification(
1,
_textEditingController.text,
"Reminder for your scheduled event at ${eventTime!.format(context)}",
eventDate!,
eventTime!,
jsonEncode({
"title": _textEditingController.text,
"eventDate": DateFormat("EEEE, d MMM y").format(eventDate!),
"eventTime": eventTime!.format(context),
}),
getDateTimeComponents(),
);
}
here is getDateTimeComponents if it matters
DateTimeComponents? getDateTimeComponents() {
if (segmentedControlGroupValue == 1) {
return DateTimeComponents.time;
} else if (segmentedControlGroupValue == 2) {
return DateTimeComponents.dayOfWeekAndTime;
}
}
it's been week since i'm trying to fix this issue.
Thank you for reading.
Here it says
Use zonedSchedule instead by passing a date in the future with the
same time and pass DateTimeComponents.matchTime as the value of the
matchDateTimeComponents parameter.
You seem to use DateTimeComponents.time correctly but I guess your date is not in the future. Can you try adding like a thousand years to your date and see? Maybe because after the first firing, the date is now in the past and it will not fire on the next day because of it.
I have successfully managed to receive FCM messages(on my mobile) via the console as well as from my NodeJs server. But how may I send & receive an FCM message that will arrive at my phone, do some tasks and then auto Cancel/Dismiss by itself?
Is this possible in Flutter with FCM?
In Android we use to have public Notification.Builder setTimeoutAfter (long durationMs)
Its more for just pinging the client app... and retrieving some data from the Apps local storage. Since it can be done automatically i want to do it without troubling the user.
These are the steps to accomplish the following
receiving a notification without disturbing the user(silently without any Alert in the system tray)
let localNotification Pkg start the progress Notification
do a background task and when finished
cancel the notification via LocalNotifications Pkg
Make sure you have the following in your .yaml file... at the time of solving this I had the following versions:
firebase_messaging: ^11.1.0
firebase_core: ^1.10.0
flutter_local_notifications: ^9.1.
For the Local Notification Package lets make a class to use its services
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService {
static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
static void initialize(BuildContext context) {
final InitializationSettings initializationSettings = InitializationSettings(
android: const AndroidInitializationSettings("#mipmap/your_icon"));
_notificationsPlugin.initialize(initializationSettings);
}
//=================================================
//==============this is the update notification
static Future<void> showProgressNotification() async {
const int maxProgress = 5;
for (int i = 0; i <= maxProgress; i++) {
await Future<void>.delayed(const Duration(seconds: 1), () async {
final AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails('progress channel', 'progress channel',
channelDescription: 'progress channel description',
channelShowBadge: false,
importance: Importance.max,
priority: Priority.high,
playSound: false,
showProgress: true,
maxProgress: maxProgress,
progress: i);
final NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await _notificationsPlugin.show(
0,//I use this id to cancel it from below method
'progress notification title',
'progress notification body',
platformChannelSpecifics,
payload: 'item x');
});
}
}
//=========================and this is for the ProgressNotification to be cancelled
static Future<void> cancelNotification() async {
await _notificationsPlugin.cancel(0);
}
}//end of class
Make your you initialize it in the init method of your Widget
#override
void initState() {
// TODO: implement initState
super.initState();
LocalNotificationService.initialize(context);
}
And lastly... this is how your Main() and top handler will look
//Receive message when app is in background/minimized
//THIS IS THE TOP LEVEL HANDLER.. as it is outside the scope of main()
Future<void> backgroundHandler(RemoteMessage message) async{
print("from the Background Handler Top Function()..............");
print(message.data.toString());
//now for the localNotification to take over
await LocalNotificationService.showProgressNotification();
await Future<void>.delayed(const Duration(seconds: 2));//faking task delay
await LocalNotificationService.cancelNotification();//by default I have made id=0
}
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(backgroundHandler);//this has to be a TOP LEVEL METHOD
runApp(MyApp());
}
And on the Server side while sending the notification make sure there is only data{}... see #Junsu Cho answer
ios is impossible
android is possible, remove notification payload
{
"to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
"notification":
{
"title": "title"
}
"data":
{
"data" : "data"
}
}
to
{
"to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
"data":
{
"data" : "data"
}
}
Inside my app the user can download some data. If that finished I would like to show a Snackbar.
I know I can show it inside a Widget with:
ScaffoldMessenger.of(context).showSnackBar(snackBar);
The thing is that the user can change Screen during the download. But the user should still be prompted with the snackbar no matter the page he is currently on.
What is the best way to achieve this? I couldn't find anything on this... Let me know if you need more info!
Not sure if this is what you are looking for but using the package below might be the simplest way to achieve it.
flutter_local_notifications: ^5.0.0+1 // I used this version to test the concept.
Edit the LocalNotificationService configuration to your need, like to silently notify the user.
Setup the package from flutter_local_notification.
// Setting up flutter_local_notifications: ^5.0.0+1 as a Service
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class LocalNotificationService {
static final instance = LocalNotificationService();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
initialize() async {
tz.initializeTimeZones();
var initializationSettingsAndroid =
AndroidInitializationSettings('ic_notification');
var initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
var initializationSettings = InitializationSettings(
iOS: initializationSettingsIOS,
android: initializationSettingsAndroid,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: (String payload) {
// Do something on notification click
return null;
}
);
}
Future<void> scheduleNotification({
int id,
String body,
String title,
String payload,
DateTime scheduledNotificationDateTime,
}) async {
final epoch = scheduledNotificationDateTime.microsecondsSinceEpoch;
var androidSpecifics = AndroidNotificationDetails(
'$epoch',
title,
body,
priority: Priority.low,
importance: Importance.min,
);
var iOSSpecifics = IOSNotificationDetails();
NotificationDetails platformChannelSpecifics = NotificationDetails(
iOS: iOSSpecifics,
android: androidSpecifics,
);
final tzTime = tz.TZDateTime.fromMicrosecondsSinceEpoch(tz.local, epoch);
await flutterLocalNotificationsPlugin.zonedSchedule(
id,
title,
body,
tzTime,
platformChannelSpecifics,
payload: payload,
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.wallClockTime,
);
}
Future onDidReceiveLocalNotification(
int i, String string1, String string2, String string3) async {}
Future cancelAllNotifications() async {
await flutterLocalNotificationsPlugin.cancelAll();
}
Future cancelNotification({int withId}) async {
await flutterLocalNotificationsPlugin.cancel(withId);
}
}
Call this function from anywhere,
LocalNotificationService.instance.scheduleNotification(
body: "This is test notification",
title: "Testing",
id: 1222,
scheduledNotificationDateTime: DateTime.now(),
);
You use the downloading function on the new dart file and put the snack bar on that page after the download succeeded you can call it. that's working now properly or use the mounting method to call the snack bar function.
Use the flush bar package to give the best output.
I have a cloud function deployed in firebase which gets triggered whenever a document is changed and then that cloud function will send a notification to the users' device who changed it, and this is the payload:
const payload = (admin.messaging.MessagingPayload = {
notification: {
title: "NOTIFICATION FROM CLOUD",
body: "Sent from triggered cloud function!!",
click_action: "FLUTTER_NOTIFICATION_CLICK",
},
data: {
title: "DATA FROM CLOUD",
body: "Sent from triggered cloud function!!",
},
});
If the application is in the background or terminated, the onBackgroundMessage of FCM should trigger and run the callback function. The function is like this:
Q. Why is this function is not even running when a notification is received in the background?
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
await _showFlutterLocalNotification(
"HEY!!",
"onBackgroundMessage ran myBackgroundMessageHandler()",
);
return Future<void>.value();
}
below is _showFlutterLocalNotification(), called inside the above function myBackgroudMessageHandler
_showFlutterLocalNotification(String title, String message) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
"DUE_DATE_REMINDER",
"Due date reminder notification",
"Notification channel for task due date reminder.",
);
var iosChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics,
iosChannelSpecifics,
);
await CustomNotificationHandler().flutterLocalNotificationsPlugin.show(
0,
title,
message,
platformChannelSpecifics,
);
}
and this is CustomNotificationHandler.dart class:
class CustomNotificationHandler {
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future initializeHandler() async {
var initializationSettingsAndroid = AndroidInitializationSettings(
"app_icon",
);
var initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
var initializationSettings = InitializationSettings(
initializationSettingsAndroid,
initializationSettingsIOS,
);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onSelectNotification: onSelectNotification,
);
}
Future onDidReceiveLocalNotification(
int id, String title, String body, String payload) async {
print("RAN onDidReceiveLocalNotification for IOS");
}
Future onSelectNotification(String payload) async {
if (payload != null) {
debugPrint("Notification payload: $payload");
}
debugPrint("OX: DONE WITH PAYLOAD");
}
}
This is my Application.kt to register FirebaseCloudMessaging plugin so that it runs in the background!
class Application : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry?) {
FirebaseMessagingPlugin.registerWith(registry?.
registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))
}
}
This is the relevant code block in AndroidManifest.xml
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="DUE_DATE_REMINDER"
/>
This is console output when the notification is sent cloud function when the app is in the background!
Have done some experiments:
Tried calling print inside myBackgroundMessageHandler() function, but it did not print anything in the console!
Tried to initialize the flutterLocalNotificationPlugin inside the _showFlutterLocalNotification()