Flutter local notification being scheduled but not being displayed

761 views Asked by At

I am making a flutter app (for android) that reminds users to take their medicines at the time scheduled by them. The user can input the 'frequency' of doses in a day, and they can schedule as many notifications as the value of frequency. For ex, if I put frequency = 2 and put time for dose 1, 10:00am and 5:00pm for dose 2, then I'll get two notifications at 10:00am and 5:00pm respectively.

The problem with my app is, the notifications are being scheduled at the right time but they're not being displayed.

I am using the flutter_local_notifications package to display the notifications.

I would also like to mention that earlier the notifications were displaying as expected but they're not being displayed after I migrated my project**. I made my project and put it in github, however, I pushed the important files containing api keys as well, as a result my project had a few clones and I saw users in database that I didn't add, so I made a whole new project in my computer and copy pasted the code from old project. Since then I'm facing the issue with notifications.

The notifications were working properly before.

Here's the github repo for reference.

Here's some details about the issue and the things I tried and their result:-

I save the data in cloud (firestore) and locally (SQLite) too so that app can work offline too. The notification class gets the data (dose time for scheduling notifications) from SQLite.

I tried showing immidiate notification, that notification was displayed! But when I schedule notifications, they're not being displayed, they're being scheduled tough(as I saw in the print statement in the debug console).

I've added the required permissions, I'm using right icon and channel name. I tried doing 'flutter clean' and 'flutter pub get' several times. I also tried restarting the computer. I also uninstalled the app from the emulator and then ran it again. The problem still didn't go away.

Since the notifications were being displayed earlier so I don't think there's any issue with the emulator. I tried running my app in my mobile as well, it's running the same as emulator. The notifications are not being displayed. Everything is working as expected.

I am using the latest versions of flutter and all other dependencies in my app.

I am using 'timezone' package for 'TZDateTime'(required by 'zonedSchedule' method of 'flutter_local_notifications' plugin), it's being initialized in the main function itself. The 'flutter_local_notifications' plugin is also initialized before use.

Here's the relevant code for reference :-

Here's the entire notifications logic :

import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:medicinereminder/Notifications/SQLite/database_helper.dart';
import 'package:medicinereminder/Notifications/SQLite/medicine_model_for_sqlite.dart';
import 'package:timezone/timezone.dart' as tz;

class NotificationServices {
  /*-------------- initializations -------------- */
  final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
      FlutterLocalNotificationsPlugin();

  final AndroidInitializationSettings _androidInitializationSettings =
      const AndroidInitializationSettings('@drawable/launcher_icon');

  void initializeNotifications() async {
    InitializationSettings initializationSettings = InitializationSettings(
      android: _androidInitializationSettings,
    );
    print("before notifications plugin initialization");
    await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
    print("notifications plugin initialized");
  }

  /*-------------- initializations - end - here -------------- */

  /*-------------- Utility functions for time data type convertion -------------- */
  
  //UTILITY FUNCTION to convert the time from DateTime format to TZDateTime format.
  tz.TZDateTime convertToTZDateTime(DateTime dateTime) {
    // Convert DateTime to local first if it's in UTC
    if (dateTime.isUtc) {
      dateTime = dateTime.toLocal();
    }
    print("current system time");
    print(tz.TZDateTime.now(tz.local));
    // Offset to adjust the DateTime to the local timezone
    // final offset = dateTime.timeZoneOffset;
    // dateTime = dateTime.add(offset);
    return tz.TZDateTime.from(dateTime, tz.local);
  }

  //UTILITY FUNCTION to convert time from TimeOfDay to DateTime format for update function
  List<DateTime> convertToDateTimeList(List<TimeOfDay> timeOfDayList) {
    DateTime currentDate = DateTime.now();
    return timeOfDayList.map((timeOfDay) {
      return DateTime(
        currentDate.year,
        currentDate.month,
        currentDate.day,
        timeOfDay.hour,
        timeOfDay.minute,
      );
    }).toList();
  }

  /*-------------- Utility functions end -------------- */

  Future<void> notificationsHelper(
      String sqliteMedicineId, String medicineName) async {
    print("reached inside of notificationsHelper");

    Medicine? medicine =
        await DatabaseHelper.instance.getMedicineById(sqliteMedicineId);
    print("got medicine details from sqlite");

    if (medicine != null) {
      List<TimeOfDay> doseTimesList = medicine.doseTimes;
      List<DateTime> validDoseTimes = [];

      for (TimeOfDay time in doseTimesList) {
        // Convert TimeOfDay to DateTime
        DateTime now = DateTime.now(); //.toLocal()
        DateTime doseDateTime =
            DateTime(now.year, now.month, now.day, time.hour, time.minute);
        validDoseTimes.add(doseDateTime);
      }

      print('Extracted Valid dose times now printing them');
      for (var dosetime in validDoseTimes) {
        print(dosetime);
      }

      print('Number of Doses: ${validDoseTimes.length}');
      print(
          'Medicine name from which notification is scheduled : $medicineName');
      print('dosage amount of the medicine : ${medicine.dosageAmount}');

      print('now calling scheduleNotification function');
      //Schedule the notification using the user's uuid to be used to make notification's unique identifier
      scheduleNotification(
        sqliteMedicineId,
        medicineName,
        medicine.dosageAmount,
        validDoseTimes,
      );
    }
  }

  void scheduleNotification(String id, String medicineName, String dosageAmount,
      List<DateTime> doseTimes) async {
    print('reached inside scheduleNotification');
    // String timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    // String uniqueString = id + timestamp;
    int baseId = id.hashCode;

    NotificationDetails notificationDetails = const NotificationDetails(
      android: AndroidNotificationDetails(
        'channel_id',
        'channel_name',
        importance: Importance.max,
        priority: Priority.high,
        ticker: 'Take medicine',
        icon: 'launcher_icon',
        color: Colors.deepPurple,
        styleInformation: BigTextStyleInformation(''),
      ),
    );
    // await _flutterLocalNotificationsPlugin.show(
    //     0, 'Test Title', 'Test body', notificationDetails);

    for (var doseIndex = 0; doseIndex < doseTimes.length; doseIndex++) {
      var doseTime = doseTimes[
          doseIndex]; //getting the dosetime in the list for processing
      tz.TZDateTime notificationTime = convertToTZDateTime(
          doseTime); //converting the doseTime from DateTime to TZDateTime to be used in the zonedSchedule function
      int uniqueNotificationId =
          baseId + doseIndex; //creating a unique id for each notification
      _flutterLocalNotificationsPlugin.zonedSchedule(
        uniqueNotificationId,
        'Medicine Reminder',
        "It's time to take $medicineName ($dosageAmount)",
        notificationTime,
        notificationDetails,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        matchDateTimeComponents: DateTimeComponents.time,
      );
      print(
          'Notification scheduled! id : $uniqueNotificationId notification time : $notificationTime');
    }
    print('out of for loop and at the end of scheduleNotification');
  }

  void updateNotification(String id, String medicineName, String dosageAmount,
      List<TimeOfDay> doseTimes) {
    cancelNotification(id);
    List<DateTime> newNotificationTimeList = convertToDateTimeList(doseTimes);
    scheduleNotification(
      id,
      medicineName,
      dosageAmount,
      newNotificationTimeList,
    );
    print("Notification Updated id : $id");
  }

  void cancelNotification(String medicineid) {
    /* Since we don't have a simple enough way to access the index of the dose to be cancelled,
     we cancel notifications for all indexes(1-10) even if those indexes do not exist.
     If they don't exist the cancel function won't do anything, so there's no downside except
     that time complexity will increase slightly. We can accept that for now for the
    sake of simplicity */
    // String timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    // String uniqueString = medicineid + timestamp;
    int baseId = medicineid.hashCode;
    for (int i = 0; i < 10; i++) {
      int uniqueNotificationId = baseId + i;
      _flutterLocalNotificationsPlugin.cancel(uniqueNotificationId);
      print("Notification canceled id : $uniqueNotificationId");
    }
  }
}

Here's the main function where the 'timezone' package is being initialized :

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  // Enabling offline persistence for Firestore
  FirebaseFirestore.instance.settings =
      const Settings(persistenceEnabled: true);

  tz.initializeTimeZones();
  runApp(
    const MyApp(),
  );
}

Here's the debug console message that's displayed after adding a new medicine : I/flutter (11823): Calling sqlite DBhelper

I/flutter (11823): inside callSqliteDBhelper

I/flutter (11823): inside insertmedicine (sqlite)

I/flutter (11823): reached just before end of insertMedicine

I/flutter (11823): reached end of SqliteDBhelper

I/flutter (11823): calling notification service

I/flutter (11823): inside callNotificationService

I/flutter (11823): reached inside of notificationsHelper

I/flutter (11823): reached end of callNotificationService

I/flutter (11823): calling addmedicine

I/flutter (11823): inside callAddmedicine

I/flutter (11823): got medicine details from sqlite

I/flutter (11823): Extracted Valid dose times now printing them

I/flutter (11823): 2023-10-15 14:20:00.000

I/flutter (11823): 2023-10-15 14:21:00.000

I/flutter (11823): Number of Doses: 2

I/flutter (11823): Medicine name from which notification is scheduled : Test 23

I/flutter (11823): dosage amount of the medicine : 1 tablet

I/flutter (11823): now calling scheduleNotification function

I/flutter (11823): reached inside scheduleNotification

I/flutter (11823): current system time

I/flutter (11823): 2023-10-15 08:48:35.067061Z

I/flutter (11823): Notification scheduled! id : 441176486 notification time : 2023-10-15 08:50:00.000Z

I/flutter (11823): current system time

I/flutter (11823): 2023-10-15 08:48:35.105574Z

I/flutter (11823): Notification scheduled! id : 441176487 notification time : 2023-10-15 08:51:00.000Z

I/flutter (11823): out of for loop and at the end of scheduleNotification

W/edicinereminde(11823): Accessing hidden field Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe; (light greylist, reflection)

I/flutter (11823): Medicine added to the database

I/flutter (11823): reached end of callAddMedicine

Since I'm converting the time to TZDateTime, the system time and notification time displayed are in the UTC time zone. AS per the print messages, the medicine is scheduled at the right time :

I/flutter (11823): current system time

I/flutter (11823): 2023-10-15 08:48:35.067061Z

I/flutter (11823): Notification scheduled! id : 441176486 notification time : 2023-10-15 08:50:00.000Z

I/flutter (11823): current system time

I/flutter (11823): 2023-10-15 08:48:35.105574Z

I/flutter (11823): Notification scheduled! id : 441176487 notification time : 2023-10-15 08:51:00.000Z

I scheduled two notifications at 2 and 3 minute gap from the current system time as seen above. The current system time shown is 08:48 and notification time are 08:50 and 08:51 respectively, that means the notifications were scheduled at right time, but they're just not being displayed. I can't figure out why. I tried almost everything I could think of, I also asked chatgpt for help, but didn't get the solution for my problem.

Any help will be appreciated! Thank you!

0

There are 0 answers