In Flutter, how do you spot images decoded at far more pixels than they are displayed, and why do they hurt performance?
answer
- decode size versus display size
- an inverted, upside-down image
- Highlight oversized images in DevTools
- 128 KB allowance by default
- multiplied across a scrolling list
basics
~20 sTurn 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 sIn 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 linesimport '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
Know the DevTools Highlight oversized images switch and what the flipped, inverted image is telling you.
Explain decode size versus display size at the device pixel ratio, and why that costs memory and cache evictions.
Sweep image-heavy screens on the largest supported device, fix assets at the source, and track memory on low-end phones.
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.