skip to content

In Flutter DevTools' frame chart, what do the UI and raster bars of a frame measure, and how do you tell which one made a frame janky?

level: middleimportance: must knowfreq 60%

answer

  1. one pair of bars per frame
  2. UI: your Dart and the framework
  3. raster: turning the layer tree into pixels
  4. about 16 ms at 60 Hz, 8 ms at 120 Hz
  5. red overlay; fix the bar that is over

basics

~20 s

Each frame has a UI bar (Dart work) and a raster bar (the engine drawing the scene). Each must fit the budget, about 16 ms at 60 Hz or 8 ms at 120 Hz; the bar over it points to the cause.

solid answer

~50 s

In the DevTools performance view each frame is a **pair of bars**. The **UI** bar is time on the UI thread running Dart: your code plus the framework's animate, build, layout and paint phases, which end in a layer tree. The **raster** bar is the engine rasterizing that layer tree through Impeller or Skia on the raster thread. The two run pipelined, so each bar has to fit the frame budget, `1000 / refresh rate` ms: about 16 ms at 60 Hz and 8 ms at 120 Hz. A long UI bar means Dart work is too expensive, such as big rebuilds, heavy layout or synchronous computation. A long raster bar means the scene is expensive to draw, such as `saveLayer`, clips, shadows or huge images. If both are over, start with the UI thread, and always measure in profile mode on a physical device.

go deeper

for a junior

Remember the budget (about 16 ms at 60 Hz, 8 ms at 120 Hz) and that frames are measured in profile mode on a real device.

for a middle

Explain what the UI and raster bars each cover, why they are judged separately, and what kind of code makes each one long.

for a senior

Go from a red frame to a cause: pick the thread, open Frame analysis and the timeline, and name the specific widgets behind the cost.

for a principal

Decide which devices and refresh rates define the team's frame budget, and how lab measurements and field data are combined.

## What one frame costs in Flutter Flutter aims to present a new frame at the display's refresh rate. The time available per frame is the **frame budget**, `1000 / X` milliseconds for an X Hz display: | Display | Budget per frame | |---|---| | 60 Hz | about 16.7 ms | | 90 Hz | about 11.1 ms | | 120 Hz | about 8.3 ms | A frame that is not ready in time is not shown on that refresh. The previous image stays on screen, and the user sees a stutter called **jank**. ## The two bars The DevTools **Flutter frames chart** draws each frame as a pair of bars: - **UI bar.** Time on the UI thread, where all Dart code runs. It covers the framework's work for the frame (animation ticks, `build`, layout, paint recording) plus any of your code that runs in that window. Its output is a **layer tree**, a lightweight description of what to draw. - **Raster bar.** Time on the raster thread, where the engine turns that layer tree into GPU commands using Impeller or Skia. You never run code on this thread, but what your Dart code asks it to draw decides how long it takes. The two threads work as a **pipeline**. While the raster thread draws frame N, the UI thread can already build frame N+1. So the rule is that **each** bar must stay under the budget, not their sum. When the sum exceeds the budget but each part fits, frames still arrive on time but one frame later, which is extra input latency rather than jank. ## Reading a janky frame 1. **Profile mode on a real device.** Debug builds run JIT code with asserts and exaggerate UI time. Emulators have different GPUs. Test on the slowest device your users might reasonably have. 2. **Find the red frames.** The chart puts a red overlay on frames that missed the target. Shader compilation frames are marked darker red; that is a separate cause with its own fixes. 3. **Compare the bars.** Whichever bar crosses the budget is the thread to investigate. If both are over, start with UI. A slow UI thread can also produce an expensive scene. 4. **Open the frame.** Selecting it fills the **Frame analysis** tab with hints about expensive operations detected in that frame, and the **Timeline events** tab with the trace. ## What each verdict usually means | Long bar | Typical causes | Where to look next | |---|---|---| | UI | rebuilding large subtrees, expensive `build` or layout, synchronous parsing or image processing in Dart | Timeline `BUILD` / `LAYOUT` / `PAINT` events, Track Widget Builds, Track Layouts, Track Paints, the CPU profiler | | raster | `saveLayer` from opacity, shader masks or backdrop filters; many clips or shadows; images decoded far larger than shown | the raster cost checklist; the Frame analysis hints | | both | a heavy first frame of a new screen, or one cause feeding the other | fix UI first, then re-measure raster | ## Common misreadings - **Adding UI and raster together** and calling a frame janky at 10 + 10 ms on a 60 Hz display. Each fits; the pipeline absorbs it. - **Using 16 ms on a 120 Hz phone.** There the budget is about 8 ms, so a frame at 12 ms is janky even though it would be fine at 60 Hz. - **Blaming the GPU for a long raster bar.** The raster thread runs on the CPU and does what your Dart code asked for. The fix is almost always in the widget tree. - **Measuring in debug mode**, where UI times include JIT compilation and asserts that release builds never run. ## Where the same numbers show up elsewhere The same two quantities appear in the on-device **PerformanceOverlay** (raster graph on top, UI graph below) and in **`FrameTiming`** as `buildDuration` and `rasterDuration`. You can read those in release builds through `SchedulerBinding.instance.addTimingsCallback`. The DevTools chart is the tool for diagnosing one session. The other two are for watching the device live and for collecting numbers from real users.

  • Why can a Flutter screen feel laggy to touch even though no frame is marked janky?
    UI and raster are pipelined, so each can fit the budget while their sum exceeds it. Frames are then presented on every refresh, but each one shows input from one frame earlier. The `SchedulerBinding.addTimingsCallback` doc describes this case: animations look smooth, touch feels sluggish. `FrameTiming.totalSpan` above the budget reveals it.
  • The raster bar is long but the UI bar is short. Why is the fix still in Dart code?
    The raster thread draws exactly the layer tree the UI thread produced. Expensive raster work comes from what the widgets asked for, such as `saveLayer` from an `Opacity` over a large subtree, many clips, or a huge image drawn small. Changing those widgets changes the scene and shortens the raster bar.

saying these in an interview costs you the question

  • A frame is janky only when UI plus raster time exceed 16 ms
  • The frame budget is 16 ms on every device, including 120 Hz ones
  • A long raster bar means the phone's GPU is too slow and nothing can be done
  • Debug-mode frame times are close enough to judge jank
  • Your Dart code runs on the raster thread when painting