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.
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;
}
}