workmanager_linux 0.1.1
workmanager_linux: ^0.1.1 copied to clipboard
Linux (experimental) implementation of workmanager using systemd user units (timers and transient units) for background task execution.
workmanager_linux #
Experimental Linux implementation of workmanager for Flutter, backed by
systemd user units. Unlike the web, Linux has a real OS scheduler, so the
plugin contract — "execute Dart code in the background, even when the app is
closed" — is implemented with actual systemd timers, not an approximation.
⚠️ EXPERIMENTAL. Read Honest limitations before using this package. Requires a systemd user session; Flatpak/Snap sandboxing is not supported yet.
How it works #
| Task type | Mechanism |
|---|---|
| One-off | Transient units via systemd-run --user --unit=workmanager-<hash> --on-active=<delay> (or --no-block for immediate runs). Transient units vanish after the task ran. |
| Periodic | A .timer/.service unit pair in ~/.config/systemd/user/. The timer uses OnUnitActiveSec for the frequency and Persistent=true for WorkManager-style catch-up of runs missed while the system was off. |
The service/timer launches your app binary in headless mode with
--background-task <taskName> --payload <path>. The payload is the
inputData JSON persisted at registration time (Android-style on-disk
payload) under $XDG_DATA_HOME/workmanager/payloads/.
There is no native code and no Pigeon: the package is pure Dart that shells
out to systemctl --user / systemd-run --user through an injectable
process runner (so all tests run without systemd).
Setup #
1. Requirements #
- A systemd-based Linux distribution with a user session.
- The user session must be able to reach the systemd user manager. When the
app is launched from a normal desktop session this just works; for
headless/autostart contexts run
loginctl enable-linger $USERso user units keep running after logout (see the caveat below). - Flatpak and Snap sandboxes cannot write user units — punted for v1.
2. Add the dependency #
dependencies:
workmanager: ^0.10.0
workmanager_linux: ^0.1.0
The main workmanager package delegates to this package on Linux
automatically, so Workmanager() works out of the box. Registering with the
main package API is all you need for scheduling:
Workmanager().initialize(callbackDispatcher);
Workmanager().registerOneOffTask("task-id", "sync", initialDelay: Duration(minutes: 5));
Workmanager().registerPeriodicTask("periodic-id", "sync", frequency: Duration(hours: 1));
3. Headless main() #
Your main() must detect the --background-task invocation and run the
callback instead of starting the UI:
Future<void> main(List<String> args) async {
if (await WorkmanagerLinux.maybeRunBackgroundTask(args, callbackDispatcher)) {
// The process was launched headless by systemd to run a background task.
// The result was logged and the process already exited (0 = success).
return;
}
runApp(const MyApp());
}
@pragma('vm:entry-point')
void callbackDispatcher() {
// Register with WorkmanagerLinux.executeTask — not Workmanager().executeTask —
// because the headless process has no native platform-channel counterpart.
WorkmanagerLinux.executeTask((taskName, inputData) async {
print("Background task: $taskName");
// Your background work here. Flutter plugins are allowed.
return true;
});
}
The dispatcher's exit code is the task result: 0 on success, 1 on failure
or when no handler was registered — so failed runs show up as failed units in
journalctl --user.
Honest limitations #
- Constraints are accepted but ignored (
networkType,requiresCharging, ...). No battery/AC/network gating in v1. - Backoff policy is accepted but ignored — failed one-off tasks are not retried. A failed periodic task simply waits for the next interval.
existingWorkPolicyis effectivelyREPLACE: re-registering a unique name overwrites the units (the previous timer is replaced).KEEPis not implemented.- Tags are accepted but not tracked, so
cancelByTagthrowsUnsupportedError(a tag registry is needed; cancel by unique name or usecancelAllinstead). - Frequency is honored as-is (no Android-style 15-minute floor; systemd
supports arbitrary intervals). Frequencies below 1 minute are not useful in
practice — systemd resolves
OnUnitActiveSecto whole seconds. - User timers stop while the user is logged out unless
loginctl enable-linger $USERis set. On many desktops, systemd user services also only run after the first graphical login of that user. registerProcessingTask,registerHealthResearchTask,registerContinuedProcessingTaskthrowUnsupportedError(iOS-only task types).- Flatpak/Snap sandboxing is unsupported (no way to write user units).
Testing #
The package is pure Dart with an injectable process runner — the test suite
asserts the exact systemctl/systemd-run commands, unit file contents,
payload round-trips and the headless argument parsing, and runs anywhere
without systemd:
cd workmanager_linux
dart test
Design #
See DESIGN.md for the decisions behind the systemd-based approach.