skip to content

In Flutter, how do you spot images decoded at far more pixels than they are displayed, and why do they hurt performance?

level: juniorimportance: should knowfreq 32%

answer

  1. decode size versus display size
  2. an inverted, upside-down image
  3. Highlight oversized images in DevTools
  4. 128 KB allowance by default
  5. multiplied across a scrolling list

basics

~20 s

Turn on DevTools' Highlight oversized images (debugInvertOversizedImages): images decoded at least 128 KB bigger than needed are drawn inverted and flipped, with a console message. They waste memory, crowd the image cache and make every draw sample a huge texture.

solid answer

~40 s

In a debug build, enable **Highlight oversized images** in the DevTools Inspector, or set `debugInvertOversizedImages = true` from `package:flutter/painting.dart`. Any image whose decoded size exceeds its displayed size, at the device pixel ratio, by more than `debugImageOverheadAllowance` (128 KB by default) is painted with inverted colors and flipped vertically, and the console reports the display size, decode size and wasted kilobytes. Oversized decodes cost memory (Flutter estimates 4 bytes per pixel plus a third for mipmaps), push other images out of the image cache so they must be decoded again, and make the GPU sample a far larger texture for a small rectangle, which adds up fast in a scrolling list. The fix is to resize the asset or ask for a smaller decode.

code

dart · 9 lines
dart
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';

void main() {
  // Debug builds only: oversized images are drawn inverted and flipped,
  // and a message with display and decode sizes is logged.
  debugInvertOversizedImages = true;
  runApp(const MaterialApp(home: Scaffold(body: Placeholder())));
}

go deeper

for a junior

Know the DevTools Highlight oversized images switch and what the flipped, inverted image is telling you.

for a middle

Explain decode size versus display size at the device pixel ratio, and why that costs memory and cache evictions.

for a senior

Sweep image-heavy screens on the largest supported device, fix assets at the source, and track memory on low-end phones.

for a principal

Set an image pipeline policy: server-side sizes per breakpoint and a budget for decoded image memory per screen.

## What "oversized" means An image file is compressed on disk. To draw it, Flutter **decodes** it into a bitmap in memory at its full pixel size unless told otherwise. If a 4000 x 3000 photo is shown as a 100 x 100 logical-pixel avatar on a screen with a device pixel ratio of 3, the screen needs only 300 x 300 physical pixels, yet the full 12-megapixel bitmap sits in memory and is scaled down every time it is drawn. Flutter's debug tooling estimates image memory as **4 bytes per pixel plus one third for mipmaps**: | | Pixels | Estimated memory | |---|---|---| | Decoded 4000 x 3000 | 12,000,000 | about 61 MB | | Displayed 300 x 300 | 90,000 | under 0.5 MB | ## How to spot it 1. Run a **debug** build (the check lives in an assert, so profile and release builds skip it). 2. In the DevTools **Inspector**, turn on **Highlight oversized images**. In code, the same switch is the top-level `debugInvertOversizedImages` flag from `package:flutter/painting.dart`. 3. Every image whose decoded size exceeds its display size by more than **`debugImageOverheadAllowance`** (default **128 KB**) is painted **with inverted colors and flipped vertically**, so it stands out immediately. 4. The console prints a message naming the image, its display size, its decode size and the extra kilobytes, and suggests a decode size to use. The display size is computed with the **highest device pixel ratio** among the app's views, so the check does not flag an image that is merely sharp enough for a high-density screen. ## Why oversized decodes hurt - **Memory.** Tens of megabytes for one thumbnail is common with camera photos. On low-end devices this leads to memory pressure and, in the worst case, the OS killing the app. - **Image cache churn.** Flutter's `ImageCache` has a default budget of 100 MiB. A few oversized decodes fill it, older entries are evicted, and scrolling back decodes them all again. - **Raster work.** Drawing a huge texture into a small rectangle means uploading and sampling far more data than the screen can show. - **Lists multiply it.** One oversized avatar is a curiosity; forty in a `ListView` are a stutter. ## Fixing what you find - **Resize the asset** at build time or on the server to the largest size it is ever displayed at. This is the best fix: smaller downloads, smaller files, smaller decodes. - When you cannot change the source, **ask Flutter to decode at a smaller size**. The console message names the parameters and values to use; how decode sizing works is a topic of its own. - Re-run with the highlight on until nothing is flipped, on the device with the largest screen you support, so you do not undersize images there. ## A note on responsive layouts The check compares against where an image is displayed **now**. An image that is a thumbnail on phones and a hero on tablets should be checked on both, and the decode sized for each, rather than decoding the tablet size everywhere.

  • Why doesn't Flutter flag an image that is twice the logical size of its widget on a 2x screen?
    The check multiplies the displayed size by the device pixel ratio, taking the highest ratio among the app's views. On a 2x screen a 100 x 100 logical widget needs 200 x 200 physical pixels, so a 200 x 200 decode is correct, not oversized.
  • Does the oversized-image highlight work in a profile build?
    No. The inversion and the console message sit inside an assert, so they only run in debug builds. Profile builds still post image-size events for tooling, but to see images flipped you must run in debug mode.

saying these in an interview costs you the question

  • An image only uses as much memory as the space it occupies on screen.
  • Oversized images only matter for download size, not for rendering.
  • The oversized-image highlight works in release builds too.
  • Any image larger than its logical widget size is oversized, whatever the pixel ratio.