Flutter Performance: Impeller and Killing Jank

FlutterPerformanceImpellerDart

Smoothness in a Flutter app comes down to one number: the frame budget. On a 60Hz screen you have roughly 16 milliseconds to build, lay out, paint, and rasterize each frame; on a 120Hz screen it's about 8. Blow that budget and the framework drops a frame, and the user feels it as jank even if they can't name it. Flutter performance work is, almost entirely, the discipline of finding what pushes frames over budget and getting it back under. The single biggest structural fix — the end of shader jank — arrived with Impeller, and the rest is profiling and rebuild hygiene.

I'll cover what Impeller changed, how to actually profile (most people do it wrong), and the concrete patterns that cause and cure stutter in real apps.

Impeller: why the first animation used to stutter

For years Flutter's most-reported performance complaint was oddly specific: an animation would stutter the first time it ran, then be smooth forever after. The cause was the old Skia backend compiling shaders lazily at runtime. The first time a particular visual effect appeared, the GPU shader for it got compiled on the spot, blowing the frame budget for those frames.

Impeller, now the default rendering engine on mobile, fixes this by precompiling its shaders ahead of time during the build. There's no runtime shader compilation to stall a frame, so animations are smooth from the very first run. If you were carrying workarounds from the Skia era — bundling shader warm-up files (--bundle-sksl-path) to pre-warm shaders — you can delete them; they're obsolete under Impeller. This is the change that quietly removed a whole category of bug reports, and it's a big part of why I described Flutter as finally smooth-by-default in the state of Flutter in 2026.

Profile in profile mode, on a real device

The most common profiling mistake is measuring in debug mode. Debug mode is not representative — assertions, no compiler optimizations, and JIT make everything slower and noisier. Always profile with:

flutter run --profile

on a physical device, not the emulator, because GPU behavior differs. Then open Flutter DevTools and go to the Performance view. What you're looking for:

That UI-vs-raster split is the first diagnostic question. It tells you whether to fix your widget tree or your painting.

Rebuilds: the usual UI-side culprit

Most UI-side jank is unnecessary rebuilds — Flutter rebuilding large subtrees when only a small part changed. The fixes are mechanical once you see them:

// Bad: setState at the top rebuilds the whole page every tick.
class _PageState extends State<Page> {
  double _scroll = 0;
  Widget build(BuildContext c) => Column(children: [
    ExpensiveHeader(opacity: 1 - _scroll / 300), // rebuilds constantly
    const HeavyList(),                            // rebuilds for no reason
  ]);
}

Three habits fix the majority of it:

Turn on the rebuild counter in DevTools (or debugProfileBuildsEnabled) to see exactly which widgets rebuild and how often. Guessing here wastes hours; the counter tells you in seconds.

Raster-side jank: what's expensive to paint

When the raster bar is the tall one, the GPU is doing too much. The usual offenders:

Cost Cheaper alternative
Opacity widget wrapping a subtree Opacity on a leaf, or AnimatedOpacity, or fade at paint time
saveLayer (clips with anti-alias, blend modes) Avoid unnecessary clipping; use borderRadius on decorations
Large blurred shadows / BackdropFilter Smaller blur radius, cache static blurs
Overdraw (many stacked opaque layers) Flatten the tree, remove hidden layers

Opacity and saveLayer are the classic traps because they force the engine to render a subtree to an offscreen buffer and composite it — expensive. Wrapping a whole animated section in Opacity is a frequent cause of raster jank; applying opacity as low in the tree as possible, or using AnimatedOpacity which is optimized, usually clears it.

Lists and images

Two more high-frequency issues in real apps:

A repeatable workflow

When someone hands me a "the app feels laggy" report, I run the same loop:

  1. Reproduce in --profile on a real mid-range device (not a flagship — users aren't all on flagships).
  2. Open DevTools Performance, find the janky frames, note UI vs raster.
  3. If UI-heavy: turn on the rebuild counter, find the over-rebuilding widget, add const, scope the state.
  4. If raster-heavy: look for Opacity, saveLayer, big shadows, overdraw; simplify the painting.
  5. Re-measure. Confirm the frame is back under budget rather than assuming the fix worked.

That measure-fix-measure loop is the whole job. Impeller removed the worst systemic cause of jank for free, which means the remaining performance work is almost always in your own widget tree and painting — and it's very findable with the profiler. Flutter gives you excellent tools to see exactly where the milliseconds go; the teams that ship smooth apps are simply the ones who look. If mobile performance under real-world conditions is your concern more broadly, I've written about handling flaky networks on mobile too.

Resources

Frequently asked questions

What problem does Impeller solve in Flutter?

Impeller eliminates shader compilation jank. The old Skia backend compiled shaders lazily at runtime, causing the first run of an animation to stutter. Impeller precompiles its shaders during the build, so animations are smooth from the very first frame.

What is the frame budget in Flutter?

On a 60Hz display you have about 16 milliseconds per frame to do all UI and raster work; on a 120Hz display it's about 8ms. Exceeding that budget drops frames, which the user perceives as jank. Profiling is about finding what pushes a frame over budget.

How do I profile Flutter performance?

Run the app in profile mode (not debug) on a real device and use Flutter DevTools. The performance view shows per-frame UI and raster timings, flags janky frames, and the CPU profiler and rebuild counter help you find expensive widgets and unnecessary rebuilds.

Hiring a senior Android / Flutter engineer?

I architect and ship production mobile software — Kotlin, Jetpack Compose, Flutter — for robotics, EV infrastructure, fintech, and real-time systems. Open to remote roles in Europe and the US.

Get in touch →