Skip to main content

Performance

Keeping the app responsive: frames rendered on time, memory that does not grow without bound, a start-up that does not test the user's patience, and a download size that does not lose them before they begin.

Why it matters. Performance is judged, not measured, by users — and they judge harshly. Start-up time and scroll smoothness correlate with retention more directly than almost any feature. The catch is that optimising the wrong thing is worse than doing nothing: it costs time and adds complexity for no gain.

What to understand

  • Whether there is a measured problem, or a suspicion
  • Which budget is being missed — frame time, memory, start-up, size
  • Where the time actually goes, from a profile rather than from intuition
  • Whether the fix is worth the complexity it adds
  • Whether it is still fixed a month later

Core topics

Frames

  • The frame budget: 16ms at 60Hz, 8ms at 120Hz
  • Build, layout, paint, raster — and which phase is over budget
  • Jank from an expensive build, and from too-wide rebuilds
  • Shader compilation jank, and what Impeller changed
  • Long lists: lazy building, item extents, and cheap items

Memory

  • Leaks: controllers, subscriptions, listeners never disposed
  • Images as the dominant cost, and decoding at display size
  • Caches without bounds
  • Reading a memory profile and telling growth from noise

Start-up

  • Cold, warm and hot start as separate numbers
  • Work done before the first frame that need not be
  • Deferred initialisation, and what must block
  • The gap between the native splash and the first real frame

Size

  • What is actually in the bundle
  • Assets, fonts, and shipping four unused weights
  • Deferred components and per-platform builds
  • Measuring rather than guessing what grew

Method

  • Profile mode, because debug numbers are meaningless
  • Real devices, and the cheapest one your users have
  • Measuring before and after, with numbers written down
  • Regression tracking, so a fix stays fixed

Levels

LevelWhat it looks like
JuniorNotices a screen is slow. Applies known good practice — const, lazy lists — without measuring.
MiddleProfiles to find the cause, distinguishes UI from raster problems, fixes leaks and image cost, measures the result.
SeniorSets budgets, tracks regressions over time, and can say what to optimise and what to leave alone.

Practice

Starting out

  • Profile, do not guess Take a screen that feels slow and find the actual cause in DevTools before changing anything.

  • Fix an image Find an image decoded far larger than it is displayed and cap it. Measure the memory.

  • Find a leak Open and close a screen fifty times and watch whether memory returns.

Going deeper

  • Make a list smooth Take a list that stutters and get it to a steady frame time, with numbers before and after.

  • Cut cold start Measure cold start on a low-end device and remove work from the critical path.

  • Track it over time Add a performance measurement to CI so a regression is visible before release.

Check yourself

  • What is your cold start time on the cheapest device your users have?
  • Which screen in your app drops the most frames, and how do you know?
  • Is your worst jank in the UI thread or the raster thread?
  • Does memory return to baseline after closing a heavy screen?
  • What is the largest thing in your app bundle?
  • What performance number do you track release over release?

Resources

  • Performance best practices — the official list, with the rare virtue of saying which items actually matter and which are folklore.
  • Flutter performance profiling — how to read the timeline and tell a UI-thread problem from a raster-thread one, which determines the entire fix.
  • DevTools — the CPU profiler, memory view and app size tool. The app size tool in particular is underused.
  • Impeller — the rendering engine and what it changed about shader jank, which used to be the most common first-run stutter.
  • Improving rendering performance — repaint boundaries, opacity and clipping costs, and the specific widgets that are expensive.