Skip to content

feat(web): bidirectional message channel between page and background worker - #708

Draft
ened wants to merge 13 commits into
mainfrom
feat/web-worker-messaging
Draft

feat(web): bidirectional message channel between page and background worker#708
ened wants to merge 13 commits into
mainfrom
feat/web-worker-messaging

Conversation

@ened

@ened ened commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds free-form two-way messaging between the app and the background worker on web, complementing request/response task execution.

  • WorkerProtocol: new message (page -> worker) and workerMessage (worker -> page) wire types
  • WorkmanagerExecution: messageHandler + sendToPage hooks on the Flutter-free dispatcher side
  • WorkmanagerWebWorker runtime: routes messages and wires sendToPage for both the dedicated Web Worker (postMessage) and the Service Worker (clients.postMessage)
  • WorkmanagerWeb: sendMessageToWorker() + workerMessages stream, including the in-page fallback path
  • Example: live worker chat with a simulated price-watch use case; demo UI simplified (tabs, auto-registered task)

No IsolateNameServer equivalent exists on web — this is the postMessage/IndexedDB communication model.

Verification

  • dart analyze clean (package + example)
  • 19 VM tests + 2 real-Chrome Web Worker round-trip tests pass (incl. new message type)
  • flutter build web builds; web/background.dart.js rebuilt

ened added 13 commits August 2, 2026 23:03
…worker

Adds free-form two-way messaging between the app and the background worker,
complementing request/response task execution:

- WorkerProtocol: new 'message' (page -> worker) and 'workerMessage'
  (worker -> page) wire types
- WorkmanagerExecution: messageHandler + sendToPage hooks on the Flutter-free
  dispatcher side
- WorkmanagerWebWorker runtime: routes messages and wires sendToPage for both
  the dedicated Web Worker (postMessage) and the Service Worker
  (clients.postMessage)
- WorkmanagerWeb: sendMessageToWorker() + workerMessages stream, including the
  in-page fallback path

The web example demonstrates the channel with a live worker chat and a
simulated price-watch use case (watch/stop/check messages, live ticks, and
background task checks). Rebuilt web/background.dart.js.
… fewer buttons

The web demo had five action buttons plus four chips stacked with the chat
and the event log. Rework it around two tabs:

- Worker chat: conversation + message input + three action chips
- Task log: single 'Run check now' action + event list

The periodic price-check task is now registered automatically on startup so
the demo works with zero setup; cancel moved to the app bar menu. Status is
a single line instead of a paragraph.
…contrast theme

Replace the BTC/ETH simulated price watch with a simulated weather watch
(cities + °C thresholds). Add a 'How this demo works' card explaining what
the demo does, what is being tested (messaging, background tasks, Service
Worker) and how to use it; explain event states in the task log. High-contrast
color scheme and larger fonts throughout. Rebuilt web/background.dart.js.
- Browser notifications when a background task finishes: the Service Worker
  shows them via registration.showNotification while the tab is closed
  (Flutter-free bundle, dart:js_interop), the page shows them while open
  (permission button on the Task log tab).
- New Guide tab: what the demo tests (messaging / background tasks /
  Service Worker) and a 6-step walkthrough that answers the key question
  explicitly — yes, close the tab; trigger Periodic Background Sync in
  DevTools; watch the notification; reopen to see the replay.
- Chat tab intro trimmed (details moved to Guide); watch thresholds tuned
  so the alert path actually fires with the simulated data.
- Rebuilt web/background.dart.js.
M3 TabBar defaults labelColor to colorScheme.primary (blue) — invisible
on the blue app bar. Set white labels + white indicator, light-blue
unselected labels via TabBarTheme.
Guide step 3 now says installing = adding the demo to the device like a
native app (what enables no-tab background execution), with both paths:
the in-app button and Chrome's address-bar install icon. Task log tab
hints when the install button isn't shown by the browser.
The install button no longer hides until Chrome fires beforeinstallprompt.
It is always on the Task log tab (and inline in the Guide): tap it to show
the browser's PWA install prompt; if Chrome has no prompt yet, a dialog
explains how to install (address-bar ⊕ icon, localhost/HTTPS requirement)
and what installing means. Tracks the appinstalled event and flips to
'App installed' when done. promptInstall() now returns whether the user
accepted.
Every executed background task now also appears in the chat as a
'background: …' line (with source), including runs that happened while
the page was closed — replayed from the persistent IndexedDB queue on
load. Task log relabeled as the persistent background queue (single
source of truth); Guide explains it (point 4) and includes a dev note
about flutter-run hot restarts being buggy on web.
Chat list was appending messages with reverse:true, pinning the view on
the oldest messages. Insert new messages at index 0 (bottom-pinned with
reverse:true) so the latest is always visible. Chat area now expands to
the full tab height instead of a fixed 220px box.
- New pages-demo workflow: builds the web demo (Flutter 3.44.8 via
  .fvmrc, melos bootstrap) with --base-href=/flutter_workmanager/ and
  force-pushes build/web to the gh-pages branch (served at
  https://fluttercommunity.github.io/flutter_workmanager/).
- WorkmanagerWeb defaults (serviceWorkerUrl/dispatcherUrl) now resolve
  against Uri.base instead of the origin root, so subpath deployments
  (Pages project sites) load the Service Worker + dispatcher bundle
  correctly; root deployments behave exactly as before.
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.

1 participant