flutter_workmanager: running Dart code after your Flutter app is closed
A Flutter plugin which allows you to execute code in the background on Android and iOS.
At a glance
- What is it?
- flutter_workmanager is a Flutter plugin that runs Dart callbacks in the background on Android and iOS through the platform schedulers, with a separate macOS and experimental web implementation. The judgement is that the plugin's real value is the cancellation and foreground-service story on newer Android, and that the recurring failure mode of background work simply not running is a platform problem the plugin documents but cannot fix.
- Who is it for?
- flutter_workmanager fits a Flutter team that needs periodic or one-off Dart work to keep running after the app is backgrounded or closed, and that is prepared to deal with the operating system's background execution limits rather than assume a timer will fire. It does not fit a team that needs exact timing, since both platforms defer and constrain background work, and it does not fit a web target, where the support is explicitly experimental.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 46 days ago.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One Dart entry point, four platform implementations
flutter_workmanager is a Flutter plugin that executes Dart code in the background, including when the app is closed, on Android and iOS. The repository is a federated plugin, and the directory layout shows the platform split plainly: `workmanager/` is the app-facing package, with `workmanager_android/`, `workmanager_apple/`, `workmanager_linux/`, `workmanager_web/` and `workmanager_platform_interface/` alongside it.
The presence of `workmanager_linux/` is worth noting because the README's platform summary mentions Android, iOS and macOS but not Linux, and the Linux package is there in the tree.
The mechanism differs by platform in a way that shapes what the plugin can promise. On Android it uses the platform's WorkManager through AlarmManager and JobScheduler, which is what the topic tags say. On iOS and macOS it uses the background task scheduling the system provides, and the README is specific about the macOS limit: on macOS, background tasks run while the app is running or backgrounded and the Mac is awake. That is a real constraint, since a scheduled task on a sleeping Mac will not fire.
Web support exists but is labelled experimental, and it comes from a separate `workmanager_web` package built on a Service Worker plus a Web Worker. The README points at a `docs/web.mdx` page and at the package's own README for what it calls honest limitations and how to test it in Chrome. The separate package and the explicit labelling are the right way to ship something that does not yet behave like the mobile implementations.
The main package is published on pub.dev as `workmanager`, with the API reference hosted there and the guides on docs.page.
Long work needs a foreground service, and a permission
Android limits how long a background worker can run, and the plugin's answer is a foreground service with a persistent notification.
The configuration is passed to either registration call through a `foregroundServiceConfig`:
await Workmanager().registerOneOffTask(
"upload-task",
"upload_files",
foregroundServiceConfig: ForegroundServiceConfig(
notificationTitle: "Uploading files",
notificationText: "Your files are being uploaded",
),
);The two task kinds map to the two schedulers. `registerOneOffTask` runs a task once, and `registerPeriodicTask` schedules recurring work. The arguments are a unique name and a task name, the latter being the string the top-level callback receives when the task runs.
The permission side of this has been a moving target, and the release history shows it. Version 0.10.8 is labelled an Android 16 FGS permission fix and was published on 2026-08-19. Version 0.10.9, on 2026-08-20, is an Android 16 one-off work hang fix. Version 0.10.10, on 2026-09-07, is a Dart-initiated background handshake fix. Three of the last three releases address something specific to a recent Android release, and the repository's last push is on 2026-09-07, the same day as 0.10.10.
That pattern is worth reading as a signal about the plugin's development: it is reactive to platform changes, which is the only viable way to be, and it is not a project that can be adopted once and forgotten. The Quick Start guide is the place the project points to for platform caveats.
The stated use cases line up with this: syncing data from an API, uploading files in the background, cleaning up old data and cache, fetching notifications, and database maintenance.
Cancelling stops the worker, not your Dart code
This is the sharpest edge in the plugin, and the documentation is direct about it.
`cancelByUniqueName`, `cancelByTag` and `cancelAll` stop the WorkManager worker immediately, but the Dart callback that is currently running keeps executing unless it reacts to the stop. The mechanism for reacting is an `onTaskStopped` handler passed to `executeTask` inside your `callbackDispatcher`:
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask(
(taskName, inputData) async {
return true;
},
onTaskStopped: (taskName, stopReason) async {
// Mark the task as cancelled / persist state, then return promptly.
},
);
}The handler receives the task name and the WorkManager stop reason, with the documented values including `cancelledByApp`, `timeout` and `preempt`. On Android below 12 the reason is `StopReason.unknown`, which is a reminder that the reason field is less informative on older devices.
The `onTaskStopped` handler is Android only. iOS has no way to stop a running task, which the documentation states plainly. So a cancellation feature you rely on behaves differently per platform, and the design of a cross-platform task should assume it may not be interruptible.
The `@pragma('vm:entry-point')` annotation on the dispatcher is a detail that matters in practice. The dispatcher is a top-level function that the platform calls by name in a new isolate, and the annotation tells the Dart compiler to keep it reachable, so it is not tree-shaken out of the binary. A dispatcher without it can work in debug and disappear in a release build, which is a class of bug that is unpleasant to diagnose.
The hard part is that the operating system may simply not run it
The plugin schedules work; it cannot compel the operating system to perform it. The project's own troubleshooting guide is titled for the question that matters most, why tasks stop after app close, and it names the causes: OEM battery optimization, constraints, and verification.
That list is a good summary of the failure modes. Vendor battery optimisation on Android is aggressive on some manufacturers and will kill a background job that runs fine on stock Android. Constraints attached to the work request can make a task ineligible to run at the moment you need it. And verification is a separate matter, since a task that appears to be scheduled is not the same as a task that executed.
The README's stated purpose is to execute Dart code in the background even when the app is closed, and on macOS it narrows that to while the app is running or backgrounded and the Mac is awake. That narrowing is the honest version of the promise, and it is the one to plan against.
The debugging guide is a separate document from the troubleshooting guide, and the distinction is a reasonable one. Debugging is about a task that ran and produced the wrong result or an error, while troubleshooting is about a task that never ran. For a background plugin, the second category is where most of the support load sits.
The example folder is described as a complete working demo with all task types and platform configurations, and it has platform directories for Android, iOS, macOS and web, an `integration_test/` directory, a `tool/` directory and its own `pubspec.yaml`.
Callback dispatch, isolates and the monorepo build
The architecture of the plugin is a Dart entry point that the platform calls, which then dispatches to your named function. `callbackDispatcher` is the conventional name for that entry point, and it is a top-level function annotated with `@pragma('vm:entry-point')`.
Inside it you call `executeTask` with the callback that does the actual work. The callback receives the task name and the input data, and returns a boolean, which the example sets to `true`. The return value is the acknowledgement that the task completed, and the platform-specific behaviour around a returned false is a detail the API documentation covers rather than the README.
The `inputData` argument is how you pass arguments to a background task, and it is the mechanism for a task that needs to know what to do. Since the callback runs in a separate isolate, the same Dart code that handles the task in the foreground has to be reachable from that isolate, which is exactly why the entry point pragma matters and why the dispatch function cannot be a closure.
The repository is managed with melos, with a `melos.yaml` at the root, and there is a `pubspec.yaml` for the main package, an `analysis_options.yaml` for the linter configuration, a `.swiftlint.yml` for the Swift side, and an `AGENTS.md` and `CONTRIBUTING.md`. The `docs/` directory holds the documentation sources with a `docs.json` configuration, and the `.fvmrc` file pins the Flutter SDK version for the project.
There is also a `CHANGELOG.md` at the root, and the change log matters for this plugin specifically, because the release titles are the quickest way to see which platform version broke what.
A community-maintained plugin with a MIT badge and no stated licence
Two things about the project's status deserve plain statement.
First, the repository is not archived and the last push was on 2026-09-07, with three releases in the preceding three weeks. The plugin is being maintained, and the recent releases are all about making it work on current Android versions.
Second, the licensing is reported as unknown, and the README badge says MIT with a link to a `LICENSE` file in the repository. A `LICENSE` file is not in the listed top-level repository entries, though, and the project metadata does not carry a licence field. So there is a mismatch between what the badge claims and what the repository listing shows. If you are shipping this plugin in a commercial app, the licence text is the thing to confirm before you rely on it, and the badge alone is not sufficient evidence.
The project lives under the `fluttercommunity` GitHub organisation, which is the community-run Flutter packages group rather than the Flutter engine team, and it has the standard support layout: GitHub Issues for bug reports, GitHub Discussions for questions, and the docs.page site for documentation.
The documentation structure is worth noting because it is unusually thorough for a plugin. There is a Quick Start guide for installation and setup, an API reference on pub.dev, a debugging guide and a troubleshooting guide, a customization guide referenced from the cancellation section, a web page for the experimental web support, and the example application. The `docs.page` site, the `docs/` directory and the `docs.json` configuration show that the documentation is versioned and published rather than kept only in the README.
Editorial conclusion
flutter_workmanager fits a Flutter team that needs periodic or one-off Dart work to keep running after the app is backgrounded or closed, and that is prepared to deal with the operating system's background execution limits rather than assume a timer will fire. It does not fit a team that needs exact timing, since both platforms defer and constrain background work, and it does not fit a web target, where the support is explicitly experimental. Verify first that your task survives on the specific devices you care about, using the troubleshooting guide the project links for the case where tasks stop after app close, that your Android manifest requests the foreground service permission your long task needs following the v0.10.8 fix, and that a cancelled task actually stops on your side, because cancelling only stops the WorkManager worker and your Dart callback keeps running until it reacts to the stop notification.
Frequently asked questions
How do I run Dart code in the background with flutter_workmanager?
Register a one-off or periodic task with `registerOneOffTask` or `registerPeriodicTask`, giving a unique name and a task name; the work lives in a top-level `callbackDispatcher` function. Inside the dispatcher you call `executeTask` with the work to perform, and mark the dispatcher with `@pragma('vm:entry-point')` so it survives into a release build.
Why do my flutter_workmanager tasks stop running after the app is closed?
The project's troubleshooting guide covers this case directly and names OEM battery optimization, work constraints and verification as the causes. The plugin schedules the work, but it cannot force the operating system to execute it, so vendor battery settings on the device are a real factor.
Does cancelling a flutter_workmanager task stop the Dart code that is running?
No. `cancelByUniqueName`, `cancelByTag` and `cancelAll` stop the WorkManager worker, but the running Dart callback keeps executing unless it reacts. You pass an `onTaskStopped` handler to `executeTask` to be notified, and that handler is Android only, since iOS has no way to stop a running task.
How do I run a long background task on Android with flutter_workmanager?
Pass a `foregroundServiceConfig` to `registerOneOffTask` or `registerPeriodicTask` with a `ForegroundServiceConfig` holding a notification title and text. The work then runs as a foreground service with a persistent notification, which is how it exceeds the regular background execution window.
Does flutter_workmanager support web and macOS?
Web support exists through a separate `workmanager_web` package using a Service Worker plus a Web Worker, and the project labels it experimental with a page describing its limitations. On macOS, background tasks run while the app is running or backgrounded and the Mac is awake.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fluttercommunity-flutter-workmanager)Was this useful?