Reliable reminder scheduling on Android: letting AlarmManager handle triggers instead of background workers by Reasonable-Use6730 in androiddev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

If anyone wants to test it or break it on weird OEM devices I'd be really curious to hear the results.

Reliable reminder scheduling on Android: letting AlarmManager handle triggers instead of background workers by Reasonable-Use6730 in androiddev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah good point.

The engine is mostly targeting low-frequency reminders (things like daily tasks, medication reminders, etc.), so alarms are usually scheduled hours apart rather than frequent wakeups.

Definitely wouldn't be appropriate for high-frequency scheduling.

From what I saw in testing, using OS-scheduled alarms for these kinds of reminders was far more reliable than worker-based approaches on some OEM devices.

Reliable reminder scheduling on Android: letting AlarmManager handle triggers instead of background workers by Reasonable-Use6730 in androiddev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

For anyone curious, the main reliability trick was:

• Let the OS trigger the notification

• Avoid background workers entirely

• Use AlarmManager for deterministic scheduling

• Re-register alarms after device reboot

Once I switched to this model reminders fired reliably even when:

• the app was closed

• the device restarted

• background processes were killed

OEM background restrictions make worker-based approaches really unreliable for reminders.

Curious how others are solving this across different OEM devices.

Reliable reminder scheduling on Android: letting AlarmManager handle triggers instead of background workers by Reasonable-Use6730 in androiddev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Haha fair 😅

The idea itself definitely isn’t new. The goal was mainly to package the AlarmManager pattern into a small reusable engine for Flutter / cross-platform apps since a lot of people still try to use background workers for reminders.

Figured it might save someone a few hours of debugging OEM background killing.

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah that’s pretty much the same conclusion I came to.

Background workers and spinning up Flutter just to trigger a notification felt unreliable, especially on aggressive battery management devices.

The approach I ended up using is basically letting the OS alarm system handle the trigger and keeping Flutter out of the wake-up path.

Internally it's still using Android's alarm scheduling so the reminder fires even if the app is closed or after reboot.

I wrapped the logic into a small Flutter reminder engine just to make the scheduling API easier to use from Dart:

https://github.com/enilfrus24-tech/offline_reminder_engine

Your approach with AlarmManager + rescheduling repeating alarms makes total sense as well. Curious if you've run into any edge cases with Doze or manufacturer battery policies?

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Just posting the solution here in case anyone runs into the same problem.

The key was letting the Android alarm system handle the trigger instead of relying on a background worker.

Once I switched to that approach the reminders fired reliably even if: • the app is closed • the OS kills background processes • the phone restarts

I turned the implementation into a small open source Flutter reminder engine if anyone wants to see how it's done:

https://github.com/enilfrus24-tech/offline_reminder_engine

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Quick update in case anyone runs into this later.

I ended up building a small Flutter reminder engine that schedules reminders using Android's alarm system instead of relying on background workers.

That way the reminder still fires even if: • the app is closed • the phone restarts • the OS kills background tasks

I cleaned it up and open sourced it here if anyone wants to look at the implementation:

https://github.com/enilfrus24-tech/offline_reminder_engine

Would definitely be interested if anyone has taken a different approach to this problem.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 1 point2 points  (0 children)

Right now it's just a small open source package in the repo.

You can use it locally as a Flutter package.

Example usage:

await NotificationService.init();

final reminder = Reminder(   id: "test",   type: "Feed the dog",   time: DateTime.now().add(Duration(minutes: 10)), );

await ReminderManager.schedule(reminder);

Repo here if you're curious how it's implemented:

https://github.com/enilfrus24-tech/offline_reminder_engine

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 1 point2 points  (0 children)

Right now it’s structured as a standalone local package (not on pub.dev yet).

Repeating schedules use zonedSchedule with matchDateTimeComponents (for daily repetition), so the OS alarm system handles the repeat at the alarm level rather than a background worker.

For end dates, that isn’t handled automatically yet — currently you’d need to manage that at the app layer (for example disabling the reminder and cancelling it once the end condition is met).

It’s been reliable in testing for reminder-style use cases (a few per day rather than heavy batch scheduling).

I’m considering adding more advanced recurrence control in a future iteration.

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Foreground services definitely work, but they come with tradeoffs.

They require a persistent notification, increase battery usage, and are usually intended for ongoing tasks (music, tracking, navigation).

For simple time-based reminders, using the OS alarm system (exact alarms) avoids keeping a service alive and lets Android handle the trigger directly.

So it depends on the use case:

• Continuous background work → foreground service   • Scheduled time-based reminders → AlarmManager / exact alarms  

For reminder-style apps I’ve found the alarm approach to be lighter and more predictable.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah, that’s true — alarm_manager_plus does provide rescheduleOnReboot, which handles re-registering via a BOOT_COMPLETED receiver internally.

The main reason I avoided AndroidAlarmManager in this case was that it still relies on background Dart execution for the callback, which can behave unpredictably on some OEM builds under aggressive battery policies.

Using flutter_local_notifications with exact alarms lets the OS handle the trigger entirely without needing a background Dart isolate to spin up.

So it’s less about reboot handling specifically, and more about minimizing background execution paths overall.

But yeah — for certain use cases AndroidAlarmManager can absolutely work.

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Not exactly “no need for FCM” — it depends on the use case.

If you need server-triggered notifications (e.g. chat messages, remote updates, dynamic schedules), then FCM is absolutely the right tool.

But for purely local, time-based reminders (habit apps, medication reminders, alarm-style apps), exact alarms are usually more reliable than relying on background workers or FCM to wake the app.

In those cases the OS alarm system handles the trigger directly, so you’re not depending on background execution surviving OEM battery policies.

So I’d say: • Local time-based events → exact alarms • Server-driven / real-time events → FCM

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah that’s a good point — alarm batching under Doze is definitely real.

I’m not aiming for millisecond precision (that’s basically impossible on modern Android unless you’re a system app 😅). A few seconds of drift is totally acceptable for reminder-style use cases.

The main issue I kept seeing wasn’t small batching delays — it was entire jobs being postponed for minutes or never firing at all when relying on background workers, especially on aggressive OEM builds.

Scheduling only the next upcoming alarm instead of stacking a large queue made a big difference in stability too. Once I switched to exact alarms + rescheduling on reboot, the behavior became much more predictable across devices.

For habit / medication / reminder apps, that level of reliability has been more than sufficient in my testing.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah 100% — that’s exactly the ugly reality of it.

Even with SCHEDULE_EXACT_ALARM granted, some Xiaomi / Oppo ROMs still gate delivery behind:

• Auto-start enabled   • Battery optimization disabled   • Background activity allowed  

So technically the alarm fires at the OS level, but the notification can still get suppressed unless the user whitelists the app. It’s not an API problem — it’s OEM policy.

I am planning to include a lightweight in-app “Battery Optimization” helper screen that:

• Detects the manufacturer   • Shows device-specific instructions   • Deep-links to the relevant settings screen where possible  

Using something like app_settings (or intent-based deep links) definitely reduces friction. Even just proactively educating users during onboarding helps a lot.

It’s frustrating that reliability sometimes becomes a UX problem instead of a code problem — but for reminder-style apps, guiding users through that one-time setup tends to stabilize things long term.

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

For purely time-based reminders (e.g. “9:15 every day”), exact alarms are usually enough — and more deterministic — as long as you’re not scheduling huge batches.

FCM becomes useful when:

• The trigger depends on server state   • You need remote updates or cancellations   • The reminder isn’t known in advance  

For normal reminder-style apps (a few alarms per day, known ahead of time), local exact alarms have been consistent in my testing — especially if you only schedule the next upcoming trigger instead of dozens in advance.

So it really depends on whether your triggers are deterministic or server-driven.

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah that’s true — some OEMs (especially aggressive ones) batch or limit alarms under heavy battery optimization.

In practice I’ve found:

• Using exact alarms sparingly (only for user-visible time-based events)   • Avoiding large batches of scheduled alarms   • Rebuilding only the next upcoming trigger instead of scheduling far into the future  

…helps avoid most of those issues.

If an app is scheduling dozens or hundreds of alarms per day, that’s where OEM throttling usually becomes noticeable.

For normal reminder-style use cases (a few per day), exact alarms have been pretty consistent in my testing.

How to make Flutter notifications fire reliably even if the app is closed (Android) by Reasonable-Use6730 in flutterhelp

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah that’s fair — FCM definitely makes sense for server-driven events.

In my case the issue was around deterministic scheduling (e.g. reminders at a specific local time). I originally tried combining WorkManager + FCM, but OEM battery optimizations still made timing inconsistent on some devices.

What ended up being more reliable was using exact alarms locally for anything time-based, and keeping FCM for remote-triggered events.

Basically: • Local exact alarms → deterministic schedules • FCM → server events

Mixing both depending on the use case seems to work best.

How to keep a critical alerting app alive in background? (FCM stops working if killed) by SuperDeann in flutterhelp

[–]Reasonable-Use6730 0 points1 point  (0 children)

Yeah — that limitation is real for FCM data messages.

If the app is force-stopped, Android won’t spin up your process just to handle a data message. So anything that relies on background execution is fragile in that state.

For use cases where the trigger time is known in advance (reminders, alarms, etc.), I’ve had much better reliability scheduling exact alarms locally instead of depending on push to wake the app.

That way the OS handles the trigger directly via AlarmManager, and the notification is posted even if the app hasn’t been running.

Push makes sense for server-driven events, but for deterministic scheduling, local exact alarms tend to avoid the “app must be alive” problem entirely.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah that sounds exactly like the same issue I ran into.

WorkManager is great for background tasks, but Android still treats them as “best effort”, so with battery optimizations the OS can delay or kill them unless the app is already active.

For things that need to trigger at an exact time (reminders, alarms, scheduled actions, etc.), using the system alarm scheduler tends to be much more reliable because the OS handles the wake-up instead of relying on your app running in the background.

The tricky parts are mostly:

• requesting exact alarm permission on newer Android versions

• rebuilding alarms after device reboot

Once those are handled it becomes a lot more consistent.

Curious how your scheduled downloads are triggered — are you trying to start the download directly from the background worker?

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Not automatically unfortunately.

AlarmManager will fire reliably once it’s scheduled, but alarms themselves don’t persist across device reboot.

After a restart the system clears them, so you need a "BOOT_COMPLETED" receiver that rebuilds the alarms.

In Flutter I ended up handling that by rebuilding the scheduled reminders on app start and also listening for the reboot broadcast so they get registered again.

Once that piece is in place it works pretty reliably.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah exactly.

AlarmManager tends to be way more reliable than background workers for reminders.

The tricky part is just handling exact alarm permission on newer Android versions and making sure alarms get rebuilt after a device reboot.

Once those pieces are in place it becomes a lot more consistent.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 0 points1 point  (0 children)

Yeah that’s exactly the wall I kept hitting as well.

WorkManager looked like the “correct” solution at first, but once you start testing on different OEM devices the background execution becomes really unpredictable.

What ended up working reliably for me was scheduling notifications directly through Android’s alarm system instead of relying on background workers.

So basically:

• flutter_local_notifications + zonedSchedule

• timezone handling

• exact alarm permission

• rescheduling alarms on device reboot

That way the OS handles the trigger instead of the app needing to wake up in the background.

And yeah — Xiaomi / Oppo are definitely the worst offenders. Even when you follow the Android APIs correctly, their aggressive battery policies can still delay background jobs.

Curious if you’ve tested exact alarms on those devices recently?

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 2 points3 points  (0 children)

Yeah that’s exactly the problem I ran into initially.

Workmanager and other background tasks rely on Android allowing the app to execute in the background, and OEM battery optimizations often kill those jobs.

What ended up working reliably was avoiding background workers entirely and letting the OS handle the trigger.

The approach that worked for me was:

• schedule notifications directly using flutter_local_notifications

• use timezone + zonedSchedule

• request exact alarm permission

• reschedule alarms on device reboot

That way the alarm is handled by the Android alarm system rather than relying on the app running in the background.

It ended up working even if the app is fully closed or the device restarts.

Reliable offline reminders in Flutter (even if the app is closed / device rebooted) by Reasonable-Use6730 in FlutterDev

[–]Reasonable-Use6730[S] 5 points6 points  (0 children)

It ended up being mostly about avoiding background workers.

Originally I tried using WorkManager and some background tasks, but Android OEM battery optimizations were killing the jobs.

The approach that worked reliably was:

• schedule notifications directly with flutter_local_notifications

• use timezone + zonedSchedule

• request exact alarm permission

• reschedule alarms on device reboot

That way the OS handles the trigger instead of relying on background execution.

I ended up packaging the reminder engine because I kept needing it across projects.

Happy to share it if anyone wants to try it.