Reputation: 113
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.
Upvotes: 1
Views: 6476
Reputation: 21
One of the ways to activate the notification in the flutter application is to use the flutter_local_notifications package. This package is designed to display notifications in daily, hourly, and every minute intervals, etc., to use this You must follow the following steps:
First of all, you must install the packages from the links below
https://pub.dev/packages/timezone
https://pub.dev/packages/flutter_local_notifications
Then add the following permissions in the AndroidManifest.xml file of your project
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
Then add the following code in the application tag of this page
<receiver
android:name=".MyWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<!-- Notification scheduling receiver -->
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver"/>
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"/>
After making these settings, you can use the following code to display the notification
In the main file of your project, define the following variable in the state section next to your previous variables
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
Then write the following functions
Future<void> _initializeNotifications() async {
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
const InitializationSettings initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
const AndroidNotificationChannel channel =
AndroidNotificationChannel(
'test_channel',
'test Notifications',
description: 'This channel is for test notifications',
importance: Importance.max,
playSound: true,
showBadge: true,
);
await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);
}
showNotifications() async {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'test_channel',
'test Notifications',
);
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await FlutterLocalNotificationsPlugin().periodicallyShow(
0,
"Test title",
"Test body 😉",
RepeatInterval.everyMinute, // everyMinute, hourly, daily, weekly
platformChannelSpecifics,
androidAllowWhileIdle: true
);
}
And finally, call the functions in initState as follows
@override
void initState() {
super.initState();
tz.initializeTimeZones(); // This will start the time zone database
_initializeNotifications();
showNotifications();
}
To use the tz variable, you need to add the following line in the mports section of your main page like this
import 'package:timezone/data/latest.dart' as tz;
With these things you have done, now you send a notification every minute. I hope your project is successful ;)
Upvotes: 0
Reputation: 113
For anyone looking for answer, here is the solution.
Main
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initNotifications();
runApp(const MyApp());
}
Future<void> initNotifications() async {
NotificationService notificationService = NotificationService();
await notificationService.init();
if (Platform.isIOS) {
await notificationService.requestIOSPermissions();
} else {
await notificationService.requestAndroidPermission();
}
}
Notifications
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_timezone/flutter_timezone.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
@pragma('vm:entry-point')
void backgroundNotificationHandler(NotificationResponse? details) async {}
Future<void> onNotificationTapped(NotificationResponse? payload) async {
}
class NotificationService {
static final NotificationService _notificationService =
NotificationService._internal();
factory NotificationService() {
return _notificationService;
}
Future<NotificationResponse?> getInitialNotification() async {
final launchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
if (launchDetails?.didNotificationLaunchApp ?? false) {
return NotificationResponse(
notificationResponseType:
NotificationResponseType.selectedNotification,
payload: launchDetails!.notificationResponse!.payload);
}
return null;
}
NotificationService._internal();
static const channelId = "1";
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
static const AndroidNotificationDetails _androidNotificationDetails =
AndroidNotificationDetails(
channelId,
"randomString",
channelDescription:
"This channel is responsible for all the local notifications",
playSound: true,
priority: Priority.high,
importance: Importance.high,
);
static const DarwinNotificationDetails _darwinNotificationDetails =
DarwinNotificationDetails();
final NotificationDetails notificationDetails = const NotificationDetails(
android: _androidNotificationDetails,
iOS: _darwinNotificationDetails,
);
void onDidReceiveLocalNotification(
int id, String? title, String? body, String? payload) async {
showDialog(
context: dialogKey.currentContext!,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text(title ?? ''),
content: Text(body ?? ''),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
child: const Text('Ok'),
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
},
)
],
),
);
}
Future<void> init() async {
const AndroidInitializationSettings androidInitializationSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
final DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification);
final InitializationSettings initializationSettings =
InitializationSettings(
android: androidInitializationSettings,
iOS: initializationSettingsDarwin,
);
// *** Initialize timezone here ***
tz.initializeTimeZones();
final String currentTimeZone = await FlutterTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(currentTimeZone));
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveBackgroundNotificationResponse: backgroundNotificationHandler,
onDidReceiveNotificationResponse: onNotificationTapped,
);
}
Future<void> requestAndroidPermission() async {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()!
.requestPermission();
}
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,
String time,
int? hours,
[DateTimeComponents? dateTimeComponents]) async {
eventDate = DateTime(eventDate.year, eventDate.month, eventDate.day);
final scheduledTime = eventDate.add(Duration(
hours: eventTime.hour,
minutes: eventTime.minute,
));
tz.TZDateTime nextInstanceOfTenAM() {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduledDate = tz.TZDateTime.from(scheduledTime, tz.local);
if (time == 'daily') {
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
} else if (time == 'hourly') {
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(Duration(hours: hours!));
}
}
return scheduledDate;
}
await flutterLocalNotificationsPlugin.zonedSchedule(
id,
title,
body,
nextInstanceOfTenAM(),
notificationDetails,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
payload: payload,
matchDateTimeComponents: dateTimeComponents,
);
}
Future<void> cancelNotification(int id) async {
await flutterLocalNotificationsPlugin.cancel(id);
}
Future<void> cancelAllNotifications() async {
await flutterLocalNotificationsPlugin.cancelAll();
}
Future<List<PendingNotificationRequest>> getNotifications() async {
final List<PendingNotificationRequest> pendingNotificationRequests =
await FlutterLocalNotificationsPlugin().pendingNotificationRequests();
return pendingNotificationRequests;
}
}
Upvotes: 0
Reputation: 12004
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.
Upvotes: 2