12

APPLICATION: Task reminders. The user creates a task and schedules when this task is due.

This package flutter_local_notification allows me to schedule a future notification to the user when they create this task. Which is fine if I schedule one notification for when the task is due.

However, if the user forgets to complete that task and the day has now past when the task was due (with the original notification been and gone). I would like the app to continue to notify the user that they haven't completed this task yet once every day, until they open the app to cancel the notification/mark the task as complete.

I'm certain that others must have already created something like this, but I'm struggling to find clear solutions online so thought I would ask here and see peoples opinions on what would be best practice.

Two solutions I can think of:


Daily background task

A background task to run once a day to check what tasks are due and overdue for that current day and then schedule a notification for some point later during that day.

Series of events

  • At 00:00 every day the app runs a background task checking what tasks are due today
  • If a task is due today, or is overdue. Schedule notification for later that day.
  • If the app is never opened, this process of checking and sending notifications repeats everyday.

Repeating notification everyday

Example Task due on "Monday"

Instead of having a background task run everyday. For this example task I'm thinking I could schedule a notification on the Monday, and at the same time also schedule a daily repeating notification for every day after (Tuesdays, Wednesday, Thursday...) indefinitely. Then if the user opens the app and completes the task, these repeating notifications are cancelled. If not, the user gets notified every day until they do.

I think this method would work, but I want to avoid scheduling so many notifications in the future and only have "todays" notifications scheduled. As this app could potentially have lots of tasks created by the user.


My flutter app is only running only on Android at the moment, but I don't want to rule out iOS. So ideally a method which works cross-platform.

All the data is stored locally, so I can't have a backend completing the data checks and sending push notifications.

Thoughts on the solutions to this problem?

Michael Johnston
  • 718
  • 2
  • 11
  • 25
  • what's the problem of using daily background tasks? – Harry Oct 09 '20 at 20:34
  • Looking into in more, the daily background task seems fine. I was curious to see if anyone has done something similar in the past and what the most efficient way to solve this might be. – Michael Johnston Oct 10 '20 at 10:33
  • 1
    How did you implement this ? I have a similar use case. – Prithvi Jan 12 '21 at 09:06
  • 2
    @Prithvi I have ended up using a package called 'background_fetch' (https://pub.dev/packages/background_fetch). I didn't find it the easiest to understand and use. But did eventually get something to work running every 24 hours checking if a notification needed to be scheduled – Michael Johnston Jan 12 '21 at 17:57
  • @MichaelJohnston. I need a task to run every midnight (12:00 AM) to check for all recurring events having reminders and push notifications for them using flutter local notification. Does background_fetch has any method, that would run specifically at given time or do i need to run it every hour and check the time. If the time == 12 am, then fire eventHandler, (something like that) – Prithvi Jan 14 '21 at 10:05
  • 1
    @Prithvi background_fetch works by setting an interval of say 24hours rather than setting it to go off at a certain time. You could run it every hour and just have a simple check on what time it is and if you need to run a function. A lot of the background_fetch functionalities and how it works is above my abilities. So I would recommend reading the docs on it :) – Michael Johnston Jan 15 '21 at 12:26

1 Answers1

5

SOLUTION 1: android_alarm_manager is the best solution to schedule background tasks. But the only disadvantage is only support Android. Let's start:

After importing this plugin to your project as usual, add the following to your AndroidManifest.xml within the tags:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

Next, within the tags, add:

<service
    android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="false"/>
<receiver
    android:name="io.flutter.plugins.androidalarmmanager.AlarmBroadcastReceiver"
    android:exported="false"/>
<receiver
    android:name="io.flutter.plugins.androidalarmmanager.RebootBroadcastReceiver"
    android:enabled="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"></action>
    </intent-filter>
</receiver>
Then in Dart code add:

import 'package:android_alarm_manager/android_alarm_manager.dart';

void printHello() {
  final DateTime now = DateTime.now();
  final int isolateId = Isolate.current.hashCode;
  print("[$now] Hello, world! isolate=${isolateId} function='$printHello'");
}

main() async {
  final int helloAlarmID = 0;
  await AndroidAlarmManager.initialize();
  runApp(...);
  await AndroidAlarmManager.periodic(const Duration(minutes: 1), helloAlarmID, printHello);
}

If you want to schedule any task every day at a specific time, you need to do something like this:

if (Platform.isAndroid) {
   await AndroidAlarmManager.periodic(
      const Duration(hours: 24), //Do the same every 24 hours
      helloAlarmID, //Different ID for each alarm
      printHello,
      wakeup: true, //the device will be woken up when the alarm fires
      startAt: DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 5, 0), //Start whit the specific time 5:00 am
      rescheduleOnReboot: true, //Work after reboot
   );
}

If alarm callbacks will need access to other Flutter plugins, including the alarm manager plugin itself, it may be necessary to inform the background service how to initialize plugins depending on which Flutter Android embedding the application is using.

This is done by giving the AlarmService a callback to call the application's onCreate method.

In particular, its Application class is as follows:

public class Application extends FlutterApplication implements PluginRegistrantCallback {
    @Override
    public void onCreate() {
        super.onCreate();
        AlarmService.setPluginRegistrant(this);
    }

    @Override
    public void registerWith(PluginRegistry registry) {
        //add AndroidAlarmManagerPlugin plugin register  if you work with arlarm
        AndroidAlarmManagerPlugin.registerWith(registry.registrarFor("io.flutter.plugins.androidalarmmanager.AndroidAlarmManagerPlugin"));
    }
}

Which must be reflected in the application's AndroidManifest.xml. E.g.:

<application
        android:name=".Application"
        ...

SOLUTION 2: Cron is another best solution to schedule background tasks. Cron run tasks periodically at fixed times, dates or intervals. But the onliy disadvantage of corn is once the app kill, cron not working in background as expected.

A simple usage example:

import 'package:cron/cron.dart';

main() {
  var cron = new Cron();
  cron.schedule(new Schedule.parse('*/3 * * * *'), () async {
    print('every three minutes');
  });
  cron.schedule(new Schedule.parse('8-11 * * * *'), () async {
    print('between every 8 and 11 minutes');
  });
}

How to setup a cronjob in general: information

Test cronjob: crontab

moe safar
  • 140
  • 2
  • 17