agentleFS
ariaraminariaramin/flutter-skills/skills/flutter-performance-jank-optimization/SKILL.md

flutter-performance-jank-optimization

Skill8 starsChanged 48 days agoMITView on GitHub

Profile, diagnose, optimize, review, and validate production Flutter performance across Android, iOS, Web, macOS, Windows, and Linux. Use this skill whenever Flutter work mentions jank, dropped or missed frames, slow scrolling, animation stutter, input latency, expensive builds/layout/paint/raster work, image decode pressure, CPU-heavy Dart work, isolates, memory/GC, startup latency, platform channels/views, Web renderer performance, battery/thermal cost, performance refactoring, or “make this Flutter app faster”—even when the user proposes a specific optimization before measuring the real bottleneck.

The file

Show its rules and text

We split this skill into the rules it gives an agent

49 rules. About 0 are common, 39 are distinctive, and 10 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈39

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 10

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read only the relevant reference for a focused task.

    line 28
  2. Distinctive:

    Keep synchronous work off interaction-critical paths when it consumes meaningful frame/input budget.

    line 38
  3. Distinctive:

    Use lazy construction for large collections.

    line 39
  4. Distinctive:

    Test representative physical hardware, including the performance floor and relevant high-refresh devices.

    line 42
  5. Distinctive:

    Never trade correctness, accessibility, hit testing, state semantics, or data freshness for a weak micro-optimization.

    line 45
  6. Distinctive:

    Treat renderer/platform internals and APIs as version-sensitive.

    line 46
  7. Distinctive:

    Search for performance-sensitive constructs such as:

    line 64
  8. Distinctive:

    Keep constant where possible:

    line 97
  9. Distinctive:

    Avoid drawing production conclusions from debug mode, simulators/emulators alone, empty fake content, a single lucky run, or only flagship …

    line 108
  10. Distinctive:

    call tree/flame chart

    line 119
  11. Distinctive:

    Do not use 16 ms as a universal target.

    line 197
  12. Distinctive:

    Run in profile mode.

    line 203
  13. Very specific:

    Use CPU/Memory/Network views only when indicated.

    line 208
  14. Distinctive:

    Use Performance Overlay as a quick indicator, not final evidence.

    line 212
  15. Distinctive:

    Do not put ordinary async I/O on an isolate merely because it takes time.

    line 226
  16. Very specific:

    Use Isolate.run or a worker only when:

    line 230
  17. Distinctive:

    Before adding selectors or state partitioning, ask:

    line 242
  18. Very specific:

    Use local state, stable child subtrees, AnimatedBuilder.child, BlocSelector, buildWhen, Provider/Riverpod selection only where they remove …

    line 249
  19. Very specific:

    Use const as good Flutter code, not as a universal jank strategy.

    line 251
  20. Distinctive:

    remove unnecessary intrinsic measurement

    line 257
  21. Very specific:

    avoid large shrinkWrap scrollables when a single lazy scroll structure fits

    line 258
  22. Distinctive:

    use known item extents only when geometry is truly stable

    line 259
  23. Distinctive:

    use slivers for large coordinated scrolling

    line 260
  24. Distinctive:

    prefer paint transforms over per-frame layout only when semantics remain correct

    line 261
  25. Distinctive:

    Do not break text scaling, wrapping, hit testing, or accessibility to force cheaper geometry.

    line 263
  26. Distinctive:

    Check UI vs raster.

    line 270
  27. Distinctive:

    Verify lazy builders/slivers.

    line 271
  28. Distinctive:

    Keep alive only content whose retained state/recreation tradeoff warrants it.

    line 284
  29. Distinctive:

    prefer right-sized server/CDN derivatives

    line 301
  30. Distinctive:

    use DPR-aware decode dimensions on supported native paths

    line 302
  31. Distinctive:

    keep layout dimensions stable

    line 304
  32. Distinctive:

    Treat Web image behavior separately.

    line 308
  33. Very specific:

    Use RepaintBoundary only where repaint isolation removes meaningful work.

    line 314
  34. Very specific:

    use repaint: Listenable where it can bypass build/layout

    line 318
  35. Very specific:

    Treat opacity, saveLayer, clips, blur, filters, and shadows as contextual costs, not banned APIs.

    line 321
  36. Distinctive:

    Check the actual backend before applying renderer-specific advice.

    line 325
  37. Distinctive:

    Do not cargo-cult historical shader warmup into current Impeller paths.

    line 327
  38. Distinctive:

    Avoid object pooling ordinary Dart objects without profiling evidence.

    line 347
  39. Very specific:

    Do not use deferFirstFrame to hide startup cost.

    line 369
  40. Distinctive:

    verify the active composition strategy

    line 381
  41. Distinctive:

    treat Android HCPP and similar modes as version/device-specific

    line 382
  42. Distinctive:

    test scrolling, animation, overlays, transforms, and lifecycle

    line 383
  43. Very specific:

    Do not assume Android/iOS/macOS platform-view behavior is interchangeable.

    line 385
  44. Distinctive:

    Do not apply stale HTML-renderer advice.

    line 400
  45. Distinctive:

    Use microbenchmarks for isolated algorithms, not as proof of screen smoothness.

    line 421
  46. Distinctive:

    Use integration/macro flows for launch, scroll, navigation, animation, or other user-visible workloads.

    line 423
  47. Distinctive:

    Avoid brittle exact microsecond thresholds on noisy shared CI.

    line 432
  48. Distinctive:

    Never claim an optimization improved performance without measurement when measurement is available.

    line 447
  49. Distinctive:

    If those answers are unavailable, profile first.

    line 483

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Flutter Performance & Jank Optimization

Optimize from evidence:

symptom → representative measurement → classify bottleneck
→ smallest effective change → re-measure → keep/revise/revert

Preserve correctness, accessibility, visual quality, and maintainability.

Reference routing

TaskReference
Measurement, frame model/budgets, build modes, devices, DevTools, frame timing, UI-isolate diagnosisreferences/01-fundamentals-profiling.md
Build/state/layout/scroll/lists/slivers/pagination/images/vector/paintreferences/02-build-layout-scroll-images.md
Animations, CustomPainter, Impeller/raster, isolates, channels, memory/GC, startup, plugins, platform viewsreferences/03-rendering-cpu-memory-startup.md
Web, desktop, battery/thermal, lifecycle, streams/input, logging, tracing, tests, regression/CIreferences/04-web-desktop-efficiency-testing.md
Anti-patterns, implementation/review/refactor workflows, production checklists, quick referencereferences/05-antipatterns-workflows-checklists.md

Read only the relevant reference for a focused task. For a broad audit, read them in order.

Operating rules

  • Measure before optimizing; do not accept intuition as proof.
  • Profile frame behavior outside debug mode.
  • Record SDK version, platform, device/browser, OS, refresh rate, renderer/backend, workload, and data size.
  • Classify before fixing: build, layout, paint, raster/GPU, image, Dart CPU, memory/GC, platform channel/view, network/I/O, startup, or Web main thread.
  • Optimize the largest measured contributor first.
  • Rebuild frequency is not itself a performance bug.
  • Keep synchronous work off interaction-critical paths when it consumes meaningful frame/input budget.
  • Use lazy construction for large collections.
  • Decode images near useful display size where supported.
  • Bound caches and retained state intentionally.
  • Test representative physical hardware, including the performance floor and relevant high-refresh devices.
  • Re-measure every meaningful change.
  • Revert optimization-only complexity if it does not materially improve the target metric.
  • Never trade correctness, accessibility, hit testing, state semantics, or data freshness for a weak micro-optimization.
  • Treat renderer/platform internals and APIs as version-sensitive.
  • Separate runtime smoothness, startup, memory, binary size, network efficiency, and battery as distinct metrics.

1. Inspect the repository

Before editing, inspect:

pubspec.yaml / pubspec.lock
Flutter/Dart constraints and actual SDK
target platforms and minimum versions
state management and DI/bootstrap
routing/page ownership
image/network stack
plugins/platform views
existing performance tests/profiles

Search for performance-sensitive constructs such as:

ListView / GridView / CustomScrollView / Sliver
shrinkWrap / IntrinsicWidth / IntrinsicHeight
scrollCacheExtent / cacheExtent
RepaintBoundary / Opacity / filters / clips
CustomPainter / AnimationController / AnimatedBuilder
Image.network / cacheWidth / cacheHeight / precacheImage
Isolate.run / compute
Timer.periodic / Stream.listen
PlatformView / AndroidView / UiKitView / WebView
runApp / deferFirstFrame

Their presence is not proof of a problem.

2. Translate the complaint into a target

Examples:

"scrolling lags" → frame stability during a defined fling
"typing is delayed" → input-to-visible-update latency
"startup is slow" → cold first-frame and/or meaningful-content time
"memory grows" → retained growth after repeatable enter/use/exit cycles
"battery drains" → sustained CPU/GPU/network wakeups

Define the metric before selecting the remedy.

3. Build a representative scenario

Keep constant where possible:

  • device/browser and OS
  • build mode
  • renderer/backend
  • refresh rate
  • dataset
  • navigation path
  • network/cache state
  • interaction

Avoid drawing production conclusions from debug mode, simulators/emulators alone, empty fake content, a single lucky run, or only flagship hardware.

4. Capture the baseline

For jank:

  • UI duration
  • raster duration
  • slow-frame timeline
  • FrameTiming.totalSpan when latency matters

For CPU:

  • call tree/flame chart
  • long synchronous interaction-path work

For memory:

  • heap trend
  • allocation rate
  • GC correlation
  • retained objects
  • native/external/image memory when relevant

For startup:

  • pre-runApp critical path
  • first frame
  • meaningful content
  • interactive readiness when relevant

For network-related UX:

  • waterfalls
  • duplicate requests
  • payload size
  • response-processing cost

Open only the profiler needed by the evidence.

5. Classify the bottleneck

UI/main isolate high

Inspect:

  • parsing, sorting, filtering, serialization
  • expensive build helpers
  • broad state recomputation
  • large collection copies/equality
  • intrinsic layout
  • large shrink-wrapped scrolling content
  • layout-changing animations
  • allocation spikes correlated with stalls

Raster high

Inspect:

  • oversized images
  • image upload/decode
  • blur/shadows/filters
  • clips and offscreen layers
  • complex paths/vectors
  • CustomPainter
  • large animated surfaces
  • platform views
  • renderer/device-specific behavior

Both high

Fix the larger measured stage first unless one change clearly reduces both.

Frames look smooth but input feels late

Inspect:

  • main-isolate event-loop work
  • FrameTiming.totalSpan
  • synchronous handlers
  • native/plugin calls
  • high-frequency events/state changes

6. Use the correct frame budget

Approximate interval:

1000 / refreshRateHz ms

Typical values:

60 Hz 16.67 ms | 90 Hz 11.11 ms | 120 Hz 8.33 ms | 144 Hz 6.94 ms

Do not use 16 ms as a universal target. Do not simply add UI and raster duration; Flutter pipelines them.

7. Use DevTools deliberately

For native targets:

  1. Run in profile mode.
  2. Record only the relevant flow.
  3. Select slow frames.
  4. Classify UI vs raster.
  5. Inspect timeline contributors.
  6. Use CPU/Memory/Network views only when indicated.
  7. Enable build/layout/paint tracing only for diagnosis.
  8. Disable extra instrumentation for final baseline/comparison.

Use Performance Overlay as a quick indicator, not final evidence. Do not recommend removed Raster Stats tooling.

For Web, use a profile web build and browser performance tooling.

8. Fix main-isolate CPU only when it is the problem

Prefer:

  • moving expensive derived computation out of build
  • computing when source data changes
  • narrowing state subscriptions only when broad updates are measurably expensive
  • canceling/superseding obsolete work
  • reducing large repeated copies/comparisons where semantics permit
  • moving genuinely heavy CPU work off the main isolate

Do not put ordinary async I/O on an isolate merely because it takes time.

Isolate gate

Use Isolate.run or a worker only when:

responsiveness gained > spawn/dispatch + transfer + coordination cost

Good candidates: large parsing, compression, crypto, image processing, large transforms. Keep tiny work local.

On Web, do not assume compute provides background CPU parallelism; use an explicit Worker architecture when required.

9. Optimize rebuilds by cost, not count

Before adding selectors or state partitioning, ask:

  • Is build time high?
  • Is a large subtree involved?
  • Does it trigger expensive layout/paint?
  • Is heavy work done in build?
  • Is the update frequency excessive?

Use local state, stable child subtrees, AnimatedBuilder.child, BlocSelector, buildWhen, Provider/Riverpod selection only where they remove meaningful work.

Use const as good Flutter code, not as a universal jank strategy.

10. Optimize layout

When layout dominates:

  • understand constraints first
  • remove unnecessary intrinsic measurement
  • avoid large shrinkWrap scrollables when a single lazy scroll structure fits
  • use known item extents only when geometry is truly stable
  • use slivers for large coordinated scrolling
  • prefer paint transforms over per-frame layout only when semantics remain correct

Do not break text scaling, wrapping, hit testing, or accessibility to force cheaper geometry.

11. Optimize scrolling systematically

For feeds/grids:

  1. Profile a deterministic fast fling.
  2. Check UI vs raster.
  3. Verify lazy builders/slivers.
  4. Inspect row build/layout.
  5. Inspect shrinkWrap/intrinsics.
  6. Inspect image source/decode dimensions.
  7. Inspect effects/CustomPainter.
  8. Inspect platform views.
  9. Inspect state emissions during scroll.
  10. Inspect keep-alive/offscreen cache.
  11. Change one contributor.
  12. Repeat the same fling.

Tune scrollCacheExtent only with evidence; more offscreen work trades latency for build/layout/memory. Verify deprecated/current APIs against the repository SDK.

Keep alive only content whose retained state/recreation tradeoff warrants it. Paginate large datasets when product semantics allow.

12. Optimize images as a pipeline

Model:

source/transfer → decode → cache → upload/raster → retained memory

A common decoded-memory estimate is:

width × height × 4 bytes

For thumbnails/feed cells:

  • prefer right-sized server/CDN derivatives
  • use DPR-aware decode dimensions on supported native paths
  • avoid original-resolution images
  • keep layout dimensions stable
  • prefetch only likely near-future assets
  • tune ImageCache only after measuring churn and memory

Treat Web image behavior separately; native decode hints do not map identically to browser decoding.

13. Optimize raster work when raster is high

Inspect images first, then effects, clipping/offscreen layers, paths/vectors, CustomPainter, platform views.

Use RepaintBoundary only where repaint isolation removes meaningful work. Do not wrap everything, and do not remove all boundaries blindly.

For CustomPainter:

  • make shouldRepaint reflect paint-affecting data
  • use repaint: Listenable where it can bypass build/layout
  • cache stable expensive geometry only when measured

Treat opacity, saveLayer, clips, blur, filters, and shadows as contextual costs, not banned APIs.

14. Verify renderer-specific assumptions

Check the actual backend before applying renderer-specific advice.

Do not cargo-cult historical shader warmup into current Impeller paths. Even with Impeller, raster jank can come from images, effects, paths, platform views, or GPU limits.

15. Diagnose memory and GC correctly

Separate:

  • live application state
  • cache retention
  • image/native memory
  • allocation churn
  • actual leaks

Frequent GC is not automatically a problem. Investigate when it correlates with stalls, large bursts, heap growth, or memory pressure.

For retained growth:

baseline snapshot → repeat lifecycle → diff → inspect growing classes/references
→ classify cache vs leak → fix ownership/eviction/allocation → re-profile

Avoid object pooling ordinary Dart objects without profiling evidence.

16. Optimize startup by critical path

Define the milestone:

process → engine → Dart → bootstrap → runApp → first frame
→ meaningful content → interactive

Inspect:

  • synchronous main
  • pre-runApp awaits
  • eager DI graph
  • DB/storage work
  • plugin initialization
  • first-screen parsing/images/vectors/fonts
  • initial build/layout/raster

Block runApp only for correctness-critical dependencies. Defer optional analytics, warming, rare feature SDKs, and secondary services when safe.

Do not use deferFirstFrame to hide startup cost; it intentionally delays presentation.

17. Handle platform integration separately

For platform channels:

  • reduce high-frequency chatter
  • batch when semantics permit
  • inspect native main-thread work
  • move heavy native processing off critical threads where supported

For platform views:

  • profile on physical target devices
  • verify the active composition strategy
  • treat Android HCPP and similar modes as version/device-specific
  • test scrolling, animation, overlays, transforms, and lifecycle

Do not assume Android/iOS/macOS platform-view behavior is interchangeable.

18. Treat Flutter Web as its own platform

Verify:

  • profile build
  • browser
  • CanvasKit vs Wasm/skwasm
  • deployment headers
  • browser main-thread work
  • JS interop
  • DOM/platform views
  • code/renderer/assets download
  • image/CDN/cache behavior

Do not apply stale HTML-renderer advice. Do not assume mobile isolate behavior applies to Web.

Benchmark --wasm only against the actual supported browser/deployment matrix.

19. Test desktop and sustained efficiency

For desktop, test large/high-DPI windows, live resize, large tables/grids, and native integrations.

For sustained workloads, inspect:

  • continuous animations
  • timers/polling
  • worker isolates
  • sensors/location
  • repeated network wakeups
  • hidden/offscreen work
  • sustained GPU effects

Pause nonessential work when hidden/backgrounded if correctness permits. Treat thermal and battery cost as performance requirements.

20. Add performance regression coverage

Use microbenchmarks for isolated algorithms, not as proof of screen smoothness.

Use integration/macro flows for launch, scroll, navigation, animation, or other user-visible workloads.

Prefer distributions:

  • p50
  • p90/p95
  • p99
  • worst frame
  • percentage above budget

Avoid brittle exact microsecond thresholds on noisy shared CI. Use controlled device labs for hard frame gates when possible.

21. Report every performance change

Use:

Symptom — what users observe
Baseline — environment and before metrics
Classification — UI/raster/CPU/memory/startup/Web/platform
Change — exact targeted modification
Reason — why it should reduce the measured cost
Trade-off — quality, memory, complexity, battery, compatibility
Verification — before/after result on the same scenario
Decision — keep, revise, or revert

Never claim an optimization improved performance without measurement when measurement is available.

Production gates

Verify:

  • representative profile/release measurements exist
  • performance-floor devices are acceptable
  • relevant high-refresh devices are tested
  • critical UI-isolate stalls are resolved
  • critical scrolling/animations meet product targets
  • raster-heavy screens are profiled
  • image sizing/decoding is appropriate
  • large collections are lazy/paginated where appropriate
  • shrinkWrap/intrinsics are deliberate
  • memory/cache behavior is understood
  • isolate overhead is justified
  • startup avoids unnecessary blocking
  • platform views are profiled on real devices
  • Web is profiled independently when supported
  • desktop high-DPI/resize is tested when supported
  • polling/animation/background work is lifecycle-aware
  • accessibility/reduced-motion behavior is preserved
  • critical flows have regression coverage where practical
  • performance-specific complexity has documented evidence

Final engineering rule

For every proposed optimization, answer:

1. What measured problem does this solve?
2. Why should this reduce that measured cost?
3. What tradeoff does it introduce?
4. How will we verify it?

If those answers are unavailable, profile first.

Prioritize: correctness → responsiveness → frame stability → input latency → memory stability → startup → battery/thermal → maintainability → raw throughput → developer convenience.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.