How to make a background app open a dialog box every day at 2pm with Flutter?

2,488

Solution 1

My solution would be using the workmanager (Current version 0.4.1) flutter package to achieve the requirement you are looking for. Since it is maintained by Flutter Community team , we can expect a long term support.

Based on their documentation

Flutter WorkManager is a wrapper around Android's WorkManager and iOS' performFetchWithCompletionHandler, effectively enabling headless execution of Dart code in the background.

This is especially useful to run periodic tasks, such as fetching remote data on a regular basis.

Android work manager will automatically handles the background processes by itself based on the OS level of version it is running.

And coming to iOS, based on their doc the feature that this package use to perform background operation is deprecated. But Flutter community team is ready to push the replacement in the upcoming release as per their comment in the GitHub repo. So upgrading to the latest version will help you to solve this issue.

Initialize the wrokmanager inside the main()

Workmanager().initialize(
      callbackDispatcher, // The top level function, aka callbackDispatcher
      isInDebugMode:
          true // If enabled it will post a notification whenever the task is running. Handy for debugging tasks
      );

and then schedule the task like below

Workmanager().registerOneOffTask("1", "simpleTask_1",
      initialDelay: Duration(seconds: 15)); 
Workmanager().registerPeriodicTask("102", "simplePeriodicTask_1",
      initialDelay: Duration(seconds: 15),
      frequency: Duration(minutes: 15)); // Set your 2 PM frequency here. 

Refer documentation and setup for available options on scheduling tasks.

And define a callbackDispatcher and it needs to be either a static function or a top level function to be accessible as a Flutter entry point as per the documentation.

//Defined outside main()
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    print(
        "Native called background task: callbackDispatcher"); //simpleTask will be emitted here.
    createNotify(); // Created a local notification to make sure everything works in background - In your case show alert here.
    return Future.value(true);
  });
}

// your main() goes here

Instead of displaying notification, derive your logic down to show the alert popup.

EDIT 1 :

To answer your questions in comments

How do you check the program only runs once per day at around 14:00:00 PM?

Scheduled work is stored in an internally managed SQLite database and WorkManager takes care of ensuring that this work persists and is rescheduled across device reboots. And this will be executed multiple times until it is cancelled manually.

Apart from this for dev purpose you can set minimum interval and verify like 1 hour once. And minimum supported interval for work manager is 15 minutes. Even if you set below that threshold time, 15 minutes is set as default.

It seems that your code does a periodic run but it doesn't run at a specific time.

This is not supported out of the box till now. But we can make use of initial delay while scheduling the task. Calculate the time difference between present time and time you want to trigger. For example , if I want to schedule some task at 9'o clock and when the scheduling the task you can do the following When app is opened at 7'o clock , then set the initial delay as two hours. Refer the stackoverflow answer related to this.

Also, with this method, are you sure it won't be automatically closed by the OS for battery saving?

According to WorkManager doc, the execution will happen during the period interval that we specify. Since WorkManager is subject to OS battery optimizations, such as doze mode we need to note that execution may be delayed but not cancelled.

Last thing: can a background process start a popup without user interaction? (other answers mention it's not really possible). Thanks again!

For me this is the tricky part. Technically, we cannot able to show UI without user actions from the background. Because, UI needs context based on which is shown to user. So coming to dart point, it may be hard to show some alert popup that you are seeking for. But if you just want to show only some sort of information, you can give it a try to FlutterToast as it will be shown to the user from background.

EDIT 2

The requirement that you are seeking to display alert during, when app is not open is called as Draw over other apps and it works only in Android as far as I guess since there is no such feature in iOS.

You can refer this package in flutter for showing alert as per your requirements. Combine the WorkManager with this package implementation to achieve your desired result. I have not tried with this package but let me know if you face any issues.

Happy Coding !!

Solution 2

TL,DR: Use alarm_manager_service. It can schedule tasks for a particular time, and if needed, have it repeat after set duration from that start time.


You were planning to have the full code in the main app. The problem is, there is no guarantee that the app will not be closed by Android OS. With, power management features (doze), the app will likely be killed in hours. Recent android versions give users explicit control. The user will have to excuse this app from power optimizations(disable battery optimization). Even so, RAM management can kill the app in critical situations. This is expected behaviour.


Now, about displaying an interface to foreground: Android also seems to discourage intrusive app behaviours like bringing an activity to the foreground that obstructs the user while he is interacting with another activity. See https://developer.android.com/guide/components/activities/background-starts. It also lists exceptions, whereby you can achieve this. (I know Facebook/messenger used to do this and all sorts of background activities even after killing tasks without respect for recommendations. But then most of us hate FB apps for being too intrusive, and have given me an unethical impression).

Running the app continuously only to check for a particular time of day is certainly waste of resource.


So you will have to register a background service apart from the main GUI app code. Dart apps run as isolates. You can create a separate isolate for the background service apart from the main GUI that the user interacts with. Now, even if the main GUI isolate is closed, the background service can run.

You'll need to modify android manifest file also when doing these. I can point you to some leads: Use an alarm_manager_service to schedule background tasks at the set time.This can then callback to carry out activity and show a dialog box or wake the app.

Another one: workmanager

Read this quick and to the point summary given as the readme description to understand the existing scenario in android OS. This actually sums up everything that I read through countless developer.android articles.

SAMPLE CODES:

You can use alarm manager to schedule tasks for a particular time, and also have it repeat after set duration from the start time ~> start at 2pm some day and repeat it periodically every 24 hours.

The codes for using alarm_manager is given here as answer (Solution1).

Solution 3

I had a similar requirement, and it's a very difficult thing to do with the requirement of drawing over other apps.

But I found an acceptable solution using the flutter_local_notifications package. You can schedule a periodic notification, and you can enable "fullScreenIntent". This link should help: https://pub.dev/packages/flutter_local_notifications#full-screen-intent-notifications

It's not perfect, but for perfect you would have to write native code using PlatformChannels:

https://flutter.dev/docs/development/platform-integration/platform-channels

Share:
2,488
Admin
Author by

Admin

Updated on December 31, 2022

Comments

  • Admin
    Admin over 1 year

    I would like an app to open a dialog box every day at 2pm, even if the user is doing something else and is not currently in the app interface at this time.

    How to do this with Flutter, such that it will work both on Android and iOS?

    I was thinking about a method like this:

    timer = Timer.periodic(Duration(seconds: 60), (Timer t) => checkTime());
    

    that checks every minute if it's between 14:00:00 and 14:00:59, with a method similar to flutter run function every x amount of seconds, but this seems like a waste of resource: there is probably better, with a more natural way to wake up an app in the background at a precise timing?

    Question: how to make an app display a dialog box at 2pm every day, even if the user is doing something else/is in another app, with Flutter?

    Note: if the device has its screen off (i.e. user is not using their phone), the dialog box should be displayed the next time the screen is turned on.

    • Nikhil Jain
      Nikhil Jain almost 3 years
      The Android platform doesn't allow to open an activity from background for applications running on Android 10 or higher. See developer.android.com/guide/components/activities/… . The ideal case is to schedule notifications using AlarmManager Service Api or JobScheduler or WorkManager Api.
    • Basj
      Basj almost 3 years
      Thanks @NikhilJain! Can you post an answer with sample code showing how to do this with Flutter (Android + iOS)?
    • Nikhil Jain
      Nikhil Jain almost 3 years
      Though I haven't tried it on Flutter but you can take reference of this link - medium.com/@fuzzymemory/… . Let me know if you face any issues.
    • Swaminathan V
      Swaminathan V almost 3 years
      @Basj posted an answer let me know if it suits your requirement.
  • Basj
    Basj almost 3 years
    Thank you @MelvinCalvin. "the app will likely be killed in hours", just for clarity, are you speaking of the general case or only of the case we use a timer like in my code example? For completeness, could you give a sample code to show how you would do it?
  • Basj
    Basj almost 3 years
    Also, it seems alarm_manager_service is useful for planning a task to run in x secondes, minutes or hours. It's not exactly what I need; instead I would like a dialog box to be displayed at a precise time each day (such as 14:00:00 pm).
  • Basj
    Basj almost 3 years
    I think it's possible, because for example the Facebook app is able to display "chat heads" even if you're not inside the FB app, for example if you are on the device desktop: example. So displaying a dialog box should be possible?
  • Basj
    Basj almost 3 years
    "You may want to create a notification to push at 14:00 to ask the user if they want to open the dialog.": how do you do this with Flutter, such that it works on iOS and Android @PierreJanineh?
  • Pierre Janineh
    Pierre Janineh almost 3 years
    Where can you see chat heads outside the Facebook app?
  • Pierre Janineh
    Pierre Janineh almost 3 years
    You can use widget-extension if you wish to show the data always on Home Screen.
  • Pierre Janineh
    Pierre Janineh almost 3 years
    "I think it's possible, because for example the Facebook app is able to display "chat heads" even if you're not inside the FB app, for example if you are on the device desktop: example. So displaying a dialog box should be possible?" That's only available for Android, iOS system does not allow Apps to take control, everything needs to be user-approved. Unless you're using a widget that could show on the user's home screen.
  • Basj
    Basj almost 3 years
    Thank you for the update @PierreJanineh. Would you have an example of Flutter code for a notification, which, once clicked, opens a dialog box with an input?
  • Melvin Calvin
    Melvin Calvin almost 3 years
    I'm also faced with this trouble. My main app uses a timer that's intended to run from user start till stopped by user. Since, my app is light, I didn't bother to create a background isolate for the periodic service, which is what would be ideal in your case. But I had to make it work by changing power saver settings for the app after installation. Otherwise it kept closing after minutes of exit, switching app or screen lock. This would only save the app until memory critical situation occurs, at which point the OS have authority to force close app. This is the case(irrespective of timer use
  • Melvin Calvin
    Melvin Calvin almost 3 years
    I cannot share a working code as I'm still in work to make another app that uses a background isolate. Dart apps run as isolates. You can create a background isolate for the background service. This is what i would recommend you inorder to make the schedule run properly and efficiently. At this point I have only articles and discussions to share, that I am reading to make it work.
  • Basj
    Basj almost 3 years
    Ok @MelvinCalvin! Please post an update if you find a solution :)
  • Melvin Calvin
    Melvin Calvin almost 3 years
    @Basj check the SO link I have added in my answer for sample code
  • Pierre Janineh
    Pierre Janineh almost 3 years
    I included the documentation link in my answer.
  • Basj
    Basj almost 3 years
    Thank you for your answer. How do you check the program only runs once per day at around 14:00:00 PM? It seems that your code does a periodic run but it doesn't run at a specific time. Also, with this method, are you sure it won't be automatically closed by the OS for battery saving? (because it runs all the time just to perform the hour check). Last thing: can a background process start a popup without user interaction? (other answers mention it's not really possible). Thanks again!
  • Swaminathan V
    Swaminathan V almost 3 years
    Please refer my EDIT 1 in my answer to your questions. @Basj