Skip to content

feat(windows): workmanager_windows — Task Scheduler based background execution (fixes #324) - #717

Draft
ened wants to merge 1 commit into
mainfrom
feat/windows-port
Draft

feat(windows): workmanager_windows — Task Scheduler based background execution (fixes #324)#717
ened wants to merge 1 commit into
mainfrom
feat/windows-port

Conversation

@ened

@ened ened commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the Windows desktop port for flutter_workmanager (tracked in #324):
a new federated package workmanager_windows implementing
WorkmanagerPlatform on top of Task Scheduler, plus minimal core
integration and docs.

v1 uses the schtasks CLI only — no COM, no C++, no win32 dependency,
pure Dart (the design doc's "schtasks CLI" option, which keeps the package
fully unit-testable off Windows).

Fixes #324

What's implemented

  • One-off tasksschtasks /Create /SC ONCE at now + initialDelay
    (minute granularity; a zero/sub-minute delay is rounded up to the next
    minute so the task always fires).
  • Periodic tasksschtasks /Create /SC DAILY /RI <minutes>; frequency
    clamped to 1 minute..416 days (the schtasks /RI range).
  • inputData persistence — Android-style on-disk payload: a JSON file
    per uniqueName under %LOCALAPPDATA%\workmanager_windows\payloads,
    handed to the headless process via --payload-file <path>.
  • cancelByUniqueName / cancelAllschtasks /End (running
    instance) + schtasks /Delete + payload cleanup; idempotent for missing
    tasks.
  • isScheduledByUniqueName / printScheduledTasks — via
    schtasks /Query (CSV parsed and filtered to tasks owned by the plugin,
    prefixed workmanager_).

Headless execution pattern

Mirrors workmanager_web/lib/execution.dart: a small Flutter-free registry
(WorkmanagerExecution) holds the handler registered by the callback
dispatcher, and a runner detects the Task Scheduler launch argument in
main():

void main(List<String> args) {
  if (WorkmanagerWindows.maybeRunBackgroundTask(args, callbackDispatcher)) {
    return;
  }
  runApp(const MyApp());
}

The runner parses --background-task <taskName>, loads the payload JSON,
invokes the registered handler, logs the result and exits with 0/1 (the
exit code becomes Task Scheduler's "Last Task Result"). The dispatcher
registers its handler through WorkmanagerWindows().executeTask(...) (the
registry), not Workmanager().executeTask(...) — the handler runs in a
separate process on Windows.

Unsupported surface (kept honest)

  • Constraints (networkType, requiresCharging, requiresDeviceIdle,
    ...) and backoff are accepted but ignored (documented no-ops) —
    schtasks does not expose WakeToRun/idle/battery/AC/network conditions.
  • existingWorkPolicy is not honored — re-registering a uniqueName
    always replaces the task (/F, which matches WorkManager's default
    REPLACE policy).
  • tag accepted but ignored; cancelByTag throws UnsupportedError.
  • iOS-only task types (registerProcessingTask, registerHealthResearchTask,
    registerContinuedProcessingTask) throw UnsupportedError (same as web).
  • Per-user tasks only: "run whether the user is logged on or not"
    requires credentials/SYSTEM and therefore admin — documented as an
    opt-in an admin can enable manually; not exposed in the v1 API.
  • Locale caveat: schtasks parses dates with the system regional
    format; v1 emits MM/DD/YYYY (en-US assumed).

Test approach

ProcessRunner is injectable (default impl is dart:io Process.run), so all
tests run without Windows:

  • test/schtasks_test.dart — command construction: /SC ONCE vs
    /SC DAILY|/RI, /TR quoting with spaces, /Delete, /Query,
    frequency clamping, date/time formatting, CSV parsing.
  • test/payload_store_test.dart — JSON payload round-trip, null inputData,
    filename sanitization (path traversal), delete/deleteAll, non-JSON
    rejection.
  • test/execution_test.dart--background-task / --payload-file arg
    parsing and runBackgroundTask exit codes (0 success / 1 failure, handler
    throws, no handler).
  • test/workmanager_windows_test.dart — full platform surface against a
    fake process runner (registration args, rollback on schtasks failure,
    cancel/cancelAll, queries, dispatcher wiring, unsupported surface).

Gates run: melos bootstrap ✓, dart analyze (no issues) ✓, dart test
(41 pure-Dart tests) + flutter test (61 total) all pass ✓,
dart format --set-exit-if-changed clean ✓. No flutter build windows run
— the package is pure Dart and needs no Windows toolchain.

Notes for reviewers

  • workmanager depends on workmanager_windows via a path dependency
    (the package is not published yet), so dart analyze on workmanager
    reports the expected invalid_dependency warning and the CI
    publishable job will fail on this draft. Once workmanager_windows is
    published, the dependency flips to a caret constraint (melos release
    handles this).
  • BackgroundTaskResult migration (feat!: BackgroundTaskResult enum instead of bool (fixes #23) #712): this PR deliberately builds
    against the current Future<bool> handler contract; the desktop packages
    will migrate to the BackgroundTaskResult enum together with feat!: BackgroundTaskResult enum instead of bool (fixes #23) #712.
  • Parallel Linux PR: a sibling PR adds workmanager_linux and touches
    the same platform-selection block in workmanager_impl.dart — expect a
    merge conflict there; trivial to resolve in either direction.
  • Windows CI: no Windows runner is configured in this repo's workflows
    yet; a windows-latest job for workmanager_windows (analyze + test) is a
    natural follow-up, as is wiring the example app.
  • A real-machine smoke test on Windows (register → wait → verify headless
    run → cancel) is recommended before this leaves draft.

…d execution

Implements WorkmanagerPlatform for Windows via the schtasks CLI (no COM,
no native code): one-off (/SC ONCE) and periodic (/SC DAILY /RI) task
registration, on-disk JSON payloads, cancellation, status queries, and a
headless --background-task runner mirroring workmanager_web's execution
registry. Includes core integration (platform selection branch), docs and
pure-Dart unit tests with an injectable process runner.
@docs-page

docs-page Bot commented Aug 3, 2026

Copy link
Copy Markdown

To preview the documentation for this pull request, visit the following URL:

docs.page/fluttercommunity/flutter_workmanager~717

Documentation is deployed and generated using docs.page

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add support to windows (flutter desktop)

1 participant