flutter-squadron-concurrency-engineering
Skill8 starsChanged 48 days agoMITView on GitHub
Design, implement, review, debug, test, migrate, and optimize production Flutter/Dart concurrency built with Squadron. Use this skill whenever work involves CPU-heavy processing, Dart isolates, Web Workers, Squadron workers or worker pools, squadron_builder code generation, marshaling custom DTOs, cancellation, worker streams, pool sizing, backpressure, worker lifecycle, stale-result races, web worker deployment, or diagnosing whether offloading improves Flutter responsiveness—even when the user only reports UI jank, slow parsing, expensive image processing, worker startup latency, queue saturation, or worker-related memory/thermal issues.
In this repository70 other files it gives its agents.
- camerawesome-flutterSkill
- dart-args-production-cliSkill
- dart-flutter-equatable-engineeringSkill
- dart-flutter-ffi-systems-engineeringSkill
- dart-metaSkill
- document-camera-frameSkill
- document-scan-flutterSkill
- figma-to-flutter-expertSkill
- flutter-animations-production-engineeringSkill
- flutter-app-hardening-reverse-engineeringSkill
- flutter-app-icon-engineeringSkill
- flutter-application-security-engineeringSkill
- flutter-app-links-deep-link-engineeringSkill
- flutter-cachingSkill
- flutter-concurrency-memory-performanceSkill
- flutter-confetti-engineeringSkill
- flutter-dart-package-engineeringSkill
- flutter-design-system-from-design-mdSkill
- flutter-dio-cache-interceptor-engineeringSkill
- flutter-dio-engineeringSkill
- flutter-doc-scannerSkill
- flutter-drift-persistence-engineeringSkill
- flutter-expert-tipsSkill
- flutter-firebase-analyticsSkill
- flutter-firebase-core-engineeringSkill
- flutter-firebase-messagingSkill
- flutter-get-it-injectable-dependency-injectionSkill
- flutter-go-router-engineeringSkill
- flutter-hive-persistence-engineeringSkill
- flutter-image-performanceSkill
- flutter-image-picker-production-engineeringSkill
- flutter-lints-static-analysis-engineeringSkill
- flutter-local-notifications-production-engineeringSkill
- flutter-memory-leak-engineeringSkill
- flutter-mobile-developmentSkill
- flutter-motion-engineeringSkill
- flutter-multi-environmentSkill
- flutter-native-splash-engineeringSkill
- flutter-package-plugin-selectionSkill
- flutter-path-provider-production-storageSkill
- flutter-performance-jank-optimizationSkill
- flutter-permission-handler-engineeringSkill
- flutter-pinputSkill
- flutter-production-build-releaseSkill
- flutter-production-engineeringSkill
- flutter-production-loggingSkill
- flutter-pro-image-editor-engineeringSkill
- flutter-secure-storage-security-engineeringSkill
- flutter-sentry-engineeringSkill
- flutter-share-plus-engineeringSkill
- flutter-skeletonizer-engineeringSkill
- flutter-supabase-engineeringSkill
- flutter-svg-engineeringSkill
- flutter-ui-performance-engineeringSkill
- flutter-url-launcher-engineeringSkill
- flutter-video-player-production-engineeringSkill
- flutter-webview-production-engineeringSkill
- freezed-production-engineeringSkill
- html-to-flutterSkill
- hydrated-blocSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
56 rules. About 0 are common, 44 are distinctive, and 12 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈44
Distinctive · estimate
This author's own choices. Worth a look.
12
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 11
Use this decision loop:
- Distinctive: line 28
Use workers mainly for meaningful CPU-bound or long-running isolated work.
- Distinctive: line 29
Do not offload ordinary HTTP, socket, storage, or timer waits merely because they are asynchronous.
- Distinctive: line 31
Treat every worker method as a serialization boundary API.
- Distinctive: line 32
Keep messages compact and task APIs coarse-grained.
- Distinctive: line 35
Use a pool only when true parallel throughput is needed.
- Distinctive: line 38
Do not set pool size mechanically to processor count.
- Distinctive: line 40
Keep worker computation pure and platform-independent where practical.
- Very specific: line 41
Never pass BuildContext, Widgets, RenderObjects, controllers, or other UI/runtime objects across workers.
- Distinctive: line 42
Verify any Flutter plugin used from workers explicitly supports the required background-isolate/platform behavior.
- Very specific: line 43
Use supported wire types or explicit SquadronMarshalers for custom DTOs.
- Distinctive: line 44
Do not claim zero-copy unless the exact platform/type path truly provides transfer semantics.
- Distinctive: line 45
Use cooperative cancellation when expensive running CPU work must actually stop.
- Very specific: line 47
Treat one worker's async methods as potentially interleaving across await points.
- Very specific: line 48
Do not assume pool calls hit the same worker.
- Distinctive: line 54
Read only what the task needs:
- Distinctive: line 64
Before changing concurrency, inspect:
- Distinctive: line 128
Do not offload an entire repository method merely because one substage is expensive.
- Distinctive: line 131
Use this default ladder:
- Distinctive: line 165
Do not benchmark only code inside the worker and call that the architecture's latency.
- Very specific: line 183
Keep core algorithms extractable as ordinary Dart logic so most correctness tests do not require workers.
- Very specific: line 184
After changing service signatures/annotations/marshalers, regenerate:
- Distinctive: line 188
Never hand-edit generated protocol files.
- Very specific: line 199
Always establish one owner responsible for stop().
- Distinctive: line 200
Do not prewarm many rare workers at app launch.
- Distinctive: line 203
Use one worker when:
- Distinctive: line 208
Use a pool when independent tasks benefit from genuine parallel throughput.
- Very specific: line 222
Do not leave that accidental in bursty production CPU paths.
- Distinctive: line 226
If producers can outpace workers, choose explicit policy:
- Very specific: line 237
Use task waitTime vs runningTime to distinguish queue pressure from slow computation.
- Distinctive: line 254
prefer a dedicated Worker.
- Distinctive: line 257
Never assume two ordinary pool calls land on the same worker.
- Distinctive: line 267
Keep marshalers compact, stable, and round-trip tested:
- Very specific: line 271
Do not send giant arbitrary domain graphs when a small DTO will do.
- Distinctive: line 276
Before moving a payload, estimate:
- Distinctive: line 282
If the same large dataset is reused repeatedly, consider whether a dedicated worker should own a worker-local copy and receive small …
- Distinctive: line 283
Only do this when lifecycle, memory, state affinity, and invalidation are explicit.
- Distinctive: line 284
Do not send a 100 MB dataset repeatedly to compute a tiny answer.
- Distinctive: line 307
Use worker streams for meaningful progressive output, not per-byte/per-iteration chatter.
- Distinctive: line 317
Keep on main isolate:
- Very specific: line 333
Do not expose Worker/Pool/Task/WorkerException in UI state unless the product explicitly manages worker infrastructure.
- Distinctive: line 334
Verify background-isolate support before using Flutter plugins inside workers.
- Very specific: line 358
Do not assume native tests or flutter build web alone prove the worker artifact is available at runtime.
- Distinctive: line 361
Treat Squadron errors as infrastructure details.
- Distinctive: line 378
Do not log full sensitive payloads.
- Distinctive: line 392
Do not assert exact completion order unless the API/business contract guarantees it.
- Distinctive: line 396
If a worker fails to start, check:
- Distinctive: line 405
If method-not-found occurs, regenerate before debugging protocol IDs.
- Distinctive: line 406
If work hangs, inspect:
- Distinctive: line 415
If worker is slower than local, inspect startup + transfer + queue + granularity before adding workers.
- Distinctive: line 416
If UI still janks, profile main-isolate preprocessing, result materialization, state mapping, widget build/layout, and raster work.
- Distinctive: line 427
Use production-sized workloads and profile/release conditions.
- Distinctive: line 440
Before upgrading Squadron or moving local code into workers:
- Distinctive: line 442
read intervening changelog/migration notes.
- Distinctive: line 452
When reviewing a repository, report findings in this order:
- Distinctive: line 492
Use Squadron only when the worker boundary produces a measurable improvement in responsiveness, throughput, or isolation that justifies its …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Flutter Squadron Concurrency Engineering
Mission
Move only the right computation off Flutter's main isolate, using the smallest justified worker boundary, message volume, concurrency, and lifecycle cost. Use this decision loop:
measure the slow stage
→ classify CPU vs I/O
→ estimate transfer/startup cost
→ choose main/Future/compute/Worker/Pool/server
→ define coarse worker API + wire types
→ bound concurrency/backpressure
→ add cancellation + stale-result protection
→ own worker lifecycle
→ test native and web separately
→ profile end-to-end + UI frames + memory/thermalsSquadron is structured worker/RPC infrastructure, not a generic wrapper around every Future.
Non-negotiable rules
- Measure before offloading.
- Use workers mainly for meaningful CPU-bound or long-running isolated work.
- Do not offload ordinary HTTP, socket, storage, or timer waits merely because they are asynchronous.
- Measure total worker cost, including startup, marshaling, transfer, queue wait, compute, and result handling.
- Treat every worker method as a serialization boundary API.
- Keep messages compact and task APIs coarse-grained.
- Batch tiny operations rather than issuing thousands of RPC calls.
- Reuse persistent workers when recurring work justifies startup amortization.
- Use a pool only when true parallel throughput is needed.
- Bound
maxWorkers; do not unintentionally leave CPU burst workloads unbounded. - For CPU-heavy pool tasks, default to
maxParallel: 1unless measurement and semantics prove otherwise. - Do not set pool size mechanically to processor count.
- Preserve CPU headroom for Flutter UI/raster work and the operating system.
- Keep worker computation pure and platform-independent where practical.
- Never pass
BuildContext, Widgets, RenderObjects, controllers, or other UI/runtime objects across workers. - Verify any Flutter plugin used from workers explicitly supports the required background-isolate/platform behavior.
- Use supported wire types or explicit
SquadronMarshalers for custom DTOs. - Do not claim zero-copy unless the exact platform/type path truly provides transfer semantics.
- Use cooperative cancellation when expensive running CPU work must actually stop.
- Reject stale results even when cancellation exists.
- Treat one worker's async methods as potentially interleaving across
awaitpoints. - Do not assume pool calls hit the same worker; use a dedicated worker or
pool.execute()for state affinity. - Own worker/pool lifecycle explicitly and call
stop(). - Build and deploy web worker artifacts explicitly; native success does not prove web correctness.
- Optimize user responsiveness, memory, battery, and thermals—not just raw throughput.
Reference routing
Read only what the task needs:
| Need | Read |
| Decide whether Squadron is appropriate; compare Future/compute/isolates; setup generation; worker lifecycle/startup | references/01-foundations-workers-lifecycle.md |
Pools, ConcurrencySettings, backpressure, stateful workers, marshaling, typed data, streams, cancellation, stale results, errors, observability | references/02-pools-state-serialization-cancellation.md |
| Flutter/Bloc/DI integration, plugins, Web Workers, JS/Wasm build/deploy, workload patterns, profiling, memory, battery/thermals | references/03-flutter-web-workloads-performance.md |
| Unit/integration/pool/web tests, debugging, migrations, behavior-preserving refactors | references/04-testing-debugging-migration.md |
| Anti-patterns, new-worker workflow, existing-code review, checklists, production gate | references/05-antipatterns-workflows-checklists.md |
Step 1 — Inspect the repository and measure the actual bottleneck
Before changing concurrency, inspect:
Flutter + Dart versions
squadron + squadron_builder versions
cancelation_token version
generated *.worker.g.dart / *.activator.g.dart files
worker ownership and stop() paths
pool ConcurrencySettings
service annotations + baseUrl + targetPlatform
custom marshalers/exceptions
payload sizes and task frequency
web worker build/deployment scripts
state-management integration
existing benchmarks/profile capturesSearch for:
@SquadronService
@SquadronMethod
WorkerPool
ConcurrencySettings
maxWorkers
maxParallel
pool.execute
CancelationToken
TimeoutToken
SquadronMarshaler
TransferableTypedData
channelLogger
stop()Then identify the precise slow stage. Prefer evidence such as:
"parsing 18 MB JSON blocks the main isolate for ~95 ms"over vague claims such as:
"loading is slow"Step 2 — Classify CPU-bound vs I/O-bound
Good worker candidates:
large parse/transform
audio/image processing
compression/decompression
CPU-heavy hashing/crypto
large sorting/aggregation
local indexing/search
binary parsing/decoding
large model transformsUsually keep ordinary waiting on async I/O:
HTTP/socket waits
database/file waits
secure-storage calls
timersFor mixed pipelines, isolate the CPU stage:
HTTP wait
→ large response decode/transform ← worker candidateDo not offload an entire repository method merely because one substage is expensive.
Step 3 — Choose the smallest execution model that fits
Use this default ladder:
| Situation | Prefer |
| Tiny synchronous work | Main isolate |
| Ordinary nonblocking I/O | Future/async I/O |
| Occasional native CPU-heavy task | Consider compute() / Isolate.run() |
| Recurring CPU-heavy operation | Persistent Squadron Worker |
| Multiple independent recurring CPU jobs | WorkerPool, after profiling |
| Cross-platform VM + Web Worker abstraction | Squadron |
| Full custom isolate protocol required | Raw Isolate |
| Better done remotely | Backend/server processing |
| A worker can be worthwhile even when elapsed task time is similar if it removes a visible main-isolate stall. |
Step 4 — Model total cost, not only worker compute time
Use:
Tworker =
startup/warmup
+ marshal input
+ transfer/copy input
+ queue wait
+ compute
+ marshal output
+ transfer/copy output
+ main-isolate postprocessingMeasure at least:
- cold first call;
- warm call;
- request/result size;
- queue wait;
- worker runtime;
- end-to-end latency;
- Flutter frame impact. Do not benchmark only code inside the worker and call that the architecture's latency.
Step 5 — Design a coarse generated service API
Expose meaningful expensive operations, not tiny implementation steps. Prefer:
resizeImage(bytes, width, height)
transformRows(batch)
search(indexQuery)
parseDocument(bytes)Avoid:
readByte()
transformOneTinyValue()
writeByte()for huge loops. Keep core algorithms extractable as ordinary Dart logic so most correctness tests do not require workers. After changing service signatures/annotations/marshalers, regenerate:
dart run build_runner build --delete-conflicting-outputsNever hand-edit generated protocol files.
Step 6 — Choose worker lifecycle deliberately
Use:
one-shot → rare + very expensive + startup justified
feature scope → expensive rarely used feature
lazy app → recurring but not every session
app scope → common recurring workload; startup amortizedWorkers auto-start on first use; call start() when you intentionally want earlier initialization or clearer startup-error handling.
Always establish one owner responsible for stop().
Do not prewarm many rare workers at app launch.
Step 7 — Decide whether a pool is actually necessary
Use one worker when:
- jobs are sequential;
- state affinity matters;
- throughput is already sufficient;
- more workers would duplicate large state. Use a pool when independent tasks benefit from genuine parallel throughput. For CPU-heavy pools start conservatively, for example:
const ConcurrencySettings(
minWorkers: 1,
maxWorkers: 2,
maxParallel: 1,
)then benchmark alternatives. Important:
maxWorkers == 0 → unbounded maximumDo not leave that accidental in bursty production CPU paths.
maxParallel controls how many tasks may be posted to one worker; it does not create extra CPU threads inside that isolate.
Step 8 — Add backpressure before increasing concurrency
If producers can outpace workers, choose explicit policy:
debounce
throttle
batch
coalesce
latest-wins
bounded upstream queue
cancel obsolete pending work
reject/drop low-value workUse task waitTime vs runningTime to distinguish queue pressure from slow computation.
A 900 ms wait and 30 ms run time is a scheduling/load problem, not an algorithm problem.
Step 9 — Treat stateful workers as an exception
Default to:
input → compute → outputState can be justified for expensive reusable indexes/tables/codecs, but introduces affinity, memory, and interleaving risks. One worker thread does not prevent async logical interleaving:
request A mutates field
→ await
request B mutates same field
→ A resumes with B's stateFor stateful multi-call operations:
- prefer a dedicated Worker; or
- reserve one pool worker with
pool.execute(); - generally keep
maxParallel: 1for exclusive stateful workflows. Never assume two ordinary pool calls land on the same worker.
Step 10 — Design wire data intentionally
Prefer:
null/bool/int/double/String
small Lists/Maps/Sets
Uint8List / typed numeric data
explicit DTO wire forms via SquadronMarshalerKeep marshalers compact, stable, and round-trip tested:
unmarshal(marshal(value)) ≈ valueDo not send giant arbitrary domain graphs when a small DTO will do.
For binary workloads prefer typed data over object-heavy List<int> when appropriate.
Native and web transfer semantics differ. Treat TransferableTypedData as a deliberate VM-specific optimization, including its creation/materialization/ownership costs; do not leak native-only assumptions into cross-platform APIs.
Step 11 — Minimize repeated large transfers
Before moving a payload, estimate:
input MB × calls
+
output MB × callsIf the same large dataset is reused repeatedly, consider whether a dedicated worker should own a worker-local copy and receive small queries. Only do this when lifecycle, memory, state affinity, and invalidation are explicit. Do not send a 100 MB dataset repeatedly to compute a tiny answer.
Step 12 — Design cancellation and stale-result safety separately
Cancellation may mean either:
caller stops caringor:
worker CPU work stopsFor expensive running CPU loops, use cooperative CancelationToken checks.
A caller-side timeout or task cancel does not automatically prove CPU work has stopped.
For interactive latest-wins flows use both:
debounce
+ cancel previous token
+ monotonically increasing revision/request ID
+ discard result if revision is staleCancellation saves resources; revision checks guarantee correctness.
Step 13 — Handle streams carefully
Use worker streams for meaningful progressive output, not per-byte/per-iteration chatter. Throttle/coalesce high-frequency progress events so message overhead does not dominate. Test:
- event order within one stream;
- completion;
- worker-side errors;
- subscription cancellation;
- cleanup.
Step 14 — Keep Flutter UI concerns on the main isolate
Keep on main isolate:
Widgets / BuildContext
navigation
rendering
state emission
framework object manipulationPreferred architecture:
Widget
→ Cubit/ViewModel
→ Repository/Application service
→ CPU processing abstraction
→ Squadron Worker/PoolDo not expose Worker/Pool/Task/WorkerException in UI state unless the product explicitly manages worker infrastructure. Verify background-isolate support before using Flutter plugins inside workers. When uncertain, keep plugin I/O on its supported isolate and offload pure CPU transformation only.
Step 15 — Treat web as a separate deployment target
Squadron web uses browser Web Workers, not native isolates. Verify:
targetPlatform
baseUrl
generated *.web.g.dart
compiled JS or Wasm worker artifact
deployed URL
CSP
custom marshaling
browser compatibilityAutomate the pipeline:
build_runner
→ compile generated web worker entry
→ flutter build web
→ verify/copy artifacts
→ deploy
→ browser worker-start smoke testDo not assume native tests or flutter build web alone prove the worker artifact is available at runtime.
Step 16 — Map errors at the processing boundary
Treat Squadron errors as infrastructure details. Preferred:
WorkerException / worker-channel failure
→ ProcessingFailure / SearchFailure / ImageFailure
→ state-management failure state
→ user-facing recoveryPreserve sanitized diagnostics:
task type
payload size
queue wait
worker runtime
worker/pool initialization
error categoryDo not log full sensitive payloads. Retry only operations proven safe to replay; do not assume worker crashes make external side effects idempotent.
Step 17 — Test correctness without depending on concurrency first
Extract pure computation and unit-test it directly. Then add boundary tests for:
- worker start/call/stop;
- result + exception propagation;
- marshaler round trips;
- cancellation;
- streams;
- pool queueing/bounds;
- state affinity if used;
- web worker artifact startup and RPC. Do not assert exact completion order unless the API/business contract guarantees it. Native tests do not replace real browser worker tests.
Step 18 — Debug from the boundary inward
If a worker fails to start, check:
versions
annotations
generated code
constructor marshaling
target platform
web artifact/baseUrlIf method-not-found occurs, regenerate before debugging protocol IDs. If work hangs, inspect:
pending vs running
queue saturation
await that never resolves
stateful interleaving/affinity
channel/worker crash
cancellationIf worker is slower than local, inspect startup + transfer + queue + granularity before adding workers. If UI still janks, profile main-isolate preprocessing, result materialization, state mapping, widget build/layout, and raster work. Squadron cannot fix a raster bottleneck.
Step 19 — Profile performance as a system
Compare appropriate variants:
A main isolate
B compute / Isolate.run
C persistent Worker
D pool size 2
E other bounded pool sizesUse production-sized workloads and profile/release conditions. Measure:
- median and tail latency;
- cold/warm latency;
- queue wait and compute time;
- Flutter frame timings;
- CPU;
- memory peaks;
- mobile battery/thermals;
- browser-specific behavior. A faster CPU benchmark that introduces scrolling jank, excessive memory, or thermal throttling is not a win.
Step 20 — Review migrations behavior-first
Before upgrading Squadron or moving local code into workers:
- record
squadronandsquadron_builderversions; - read intervening changelog/migration notes;
- regenerate code;
- characterize results/errors/ordering/cancellation first;
- rerun native integration tests;
- rebuild web worker artifacts;
- run browser tests;
- benchmark representative workloads. Worker-boundary changes can alter timing, copying, object identity, exception shape, cancellation, state affinity, and plugin availability even when public business signatures look unchanged.
Existing-code review output
When reviewing a repository, report findings in this order:
- Workload suitability — which stages are actually CPU-bound and measured.
- Execution strategy — whether main/compute/Worker/Pool is justified.
- Lifecycle — startup, ownership, and missing
stop()paths. - Pool policy — worker bounds,
maxParallel, queue/backpressure. - Message boundary — payload sizes, granularity, marshalers, native/web differences.
- Concurrency correctness — async interleaving, affinity, stale results, cancellation.
- Flutter integration — UI/plugin boundary violations.
- Web deployment — generated artifacts, base URL, CSP, browser verification.
- Performance evidence — cold/warm, queue, frames, memory, thermals.
- Tests/migration risk — missing boundary, browser, or regression coverage. For each problem give:
finding
→ why it matters
→ evidence
→ smallest safe change
→ verificationProduction definition of done
A Squadron change is production-ready only when:
- workload suitability is demonstrated;
- main-isolate baseline and end-to-end worker behavior are measured;
- worker/pool lifecycle and
stop()ownership are explicit; - concurrency is bounded;
- queue/backpressure policy exists for bursty producers;
- task granularity is sensible;
- payload sizes and marshaling are reviewed;
- custom DTOs round-trip correctly;
- stateful worker interleaving/affinity is deliberate;
- long-running obsolete CPU jobs can stop when required;
- stale results cannot overwrite newer state;
- worker errors map to application failures;
- plugin/background-isolate assumptions are verified;
- web artifacts are generated, deployed, and browser-tested when web is supported;
- profile/release measurements include frames and memory;
- low-end/mobile thermal implications are acceptable;
- generated code and package versions are compatible;
- pure logic and worker-boundary tests pass. Use Squadron only when the worker boundary produces a measurable improvement in responsiveness, throughput, or isolation that justifies its communication and lifecycle cost.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

