skip to content

Image Providers & Cache

Image.asset, network, file and memory all resolve an ImageProvider into the ImageCache, and cacheWidth decodes at display size. Interviewers probe loading states, caching and memory spikes.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

6

In Flutter, how do Image.asset, Image.network, Image.file and Image.memory differ, and what do all four have in common?

level: juniorimportance: must knowfreq 66%

answer

  1. each wraps an ImageProvider
  2. AssetImage, NetworkImage, FileImage, MemoryImage
  3. one shared in-memory ImageCache
  4. network: no disk cache; CORS on web
  5. same builders and cacheWidth on all

basics

~20 s

Each constructor wraps a different ImageProvider: AssetImage for bundled files, NetworkImage for URLs, FileImage for device files and MemoryImage for bytes. All decode through the same in-memory ImageCache and accept cacheWidth, cacheHeight, the loading, frame and error builders, and gaplessPlayback.

solid answer

~40 s

`Image` is one widget; the named constructors only choose its `ImageProvider`. `Image.asset` uses `AssetImage` (or `ExactAssetImage` when you pass `scale`) and resolves resolution variants from the app bundle. `Image.network` uses `NetworkImage`, which on mobile and desktop issues an HTTP GET through a shared `dart:io` `HttpClient`, sends optional `headers`, and fails with `NetworkImageLoadException` on a non-200 status. `Image.file` uses `FileImage` and reads a `dart:io` `File`, so it is unavailable on the web. `Image.memory` uses `MemoryImage` over a `Uint8List`. All four share the global `ImageCache`, which holds decoded images in memory only: `NetworkImage` writes nothing to disk, so a cold start downloads the photo again.

code

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

class ListingPhoto extends StatelessWidget {
  const ListingPhoto({super.key, required this.url, required this.token});

  final String url;
  final String token;

  @override
  Widget build(BuildContext context) {
    return Image.network(
      url,
      headers: {'Authorization': 'Bearer $token'},
      fit: BoxFit.cover,
      errorBuilder: (context, error, stackTrace) =>
          Image.asset('assets/images/listing_placeholder.png', fit: BoxFit.cover),
    );
  }
}

go deeper

for a junior

Name the four constructors, their sources and providers, and know that errorBuilder and a placeholder are expected for network images.

for a middle

Explain that every constructor goes through an ImageProvider key and the shared in-memory ImageCache, and what that means for repeat displays and restarts.

for a senior

Account for platform differences, such as web CORS, ignored cacheWidth on web and no disk cache, when choosing how a photo-heavy app loads images.

for a principal

Decide where image caching lives in the app's architecture, in memory, on disk or at a CDN, and which layer owns each concern.

## One widget, four providers `Image` is a single `StatefulWidget` whose job is to display whatever an **`ImageProvider`** produces. The four named constructors differ only in which provider they create: | Constructor | Provider | Source | Cache key built from | |---|---|---|---| | `Image.asset(name)` | `AssetImage` (`ExactAssetImage` if `scale` is given) | the app's asset bundle | bundle, asset name, chosen variant | | `Image.network(url)` | `NetworkImage` | an HTTP(S) URL | URL, scale, headers | | `Image.file(file)` | `FileImage` | a `dart:io` `File` on the device | file path, scale | | `Image.memory(bytes)` | `MemoryImage` | a `Uint8List` already in memory | the very same `Uint8List` instance, scale | An `ImageProvider` knows how to produce a **key** describing the exact image, and how to load and decode the bytes for that key. The widget asks the provider to **resolve** the image, and the provider goes through the global **`ImageCache`** first: if a decoded image for the same key is already there, it is reused; if not, loading starts and the result is cached. ## What each one does differently - **`Image.asset`** reads from `DefaultAssetBundle.of(context)`, so it follows a substituted bundle, and picks the resolution variant (`2.0x`, `3.0x`) closest to the device pixel ratio. Assets from packages take a `package` argument. - **`Image.network`** fetches the URL. On mobile and desktop it uses one shared `HttpClient` from `dart:io`, adds any `headers` you pass (for example an authorization header for private listing photos), reports download progress to `loadingBuilder`, and throws `NetworkImageLoadException` with the status code when the server does not answer 200. A failed load is evicted from the cache so the next attempt tries the network again. On Android, release builds need the internet permission in the manifest. - **`Image.file`** reads a local file, typically a photo the user just took. `dart:io` files do not exist on the web. - **`Image.memory`** decodes bytes you already hold, for example a thumbnail embedded in an API response. ## What they share All four constructors accept the same display and loading options: - `width`, `height`, `fit`, `alignment` for layout and painting; - **`cacheWidth` / `cacheHeight`**, which make the engine decode at a smaller size to save memory; - **`frameBuilder`**, **`loadingBuilder`** (useful mainly for network images) and **`errorBuilder`** for placeholders, progress and fallbacks; - **`gaplessPlayback`**, which keeps the old image visible while a new provider loads; - `filterQuality`, defaulting to `FilterQuality.medium`. ## Deferred loading while scrolling Every `Image` wraps its provider in a **`ScrollAwareImageProvider`** before resolving it. If the image is already in the cache, it resolves at once. Otherwise, while the enclosing scrollable is moving at high velocity, it waits frame by frame and starts loading only when scrolling slows, and it gives up if the widget has been disposed by then. A fast fling through a long gallery therefore does not start a download for every photo that flashes past. You get this for free with any of the four constructors. ## The cache is memory only Flutter's `ImageCache` holds **decoded** images in memory, by default up to 1000 entries and 100 MiB. It does not persist anything. `NetworkImage` downloads the bytes, decodes them and keeps only the decoded image in that cache, so: 1. Scrolling back to a photo seen a minute ago is instant, if the cache has not evicted it. 2. After the app restarts, or after a memory-pressure clear, the same photo is downloaded again. 3. Disk caching for a real-estate gallery, where users revisit listings, needs a separate caching layer or package on top. ## Platform notes for Image.network on the web - The browser's cross-origin rules apply: an image host that does not allow the app's origin makes the fetch fail. - `webHtmlElementStrategy` (default `WebHtmlElementStrategy.never`) can display such images through an HTML element instead, at the cost of performance and of options like `headers`, `opacity` and filtering. - `cacheWidth` and `cacheHeight` are ignored for network images on the web, because the browser does the decoding. ## Choosing one | Situation | Constructor | |---|---| | Logo, placeholder, onboarding art shipped with the app | `Image.asset` | | Listing photos from the server | `Image.network` | | A photo the agent just took with the camera | `Image.file` | | A thumbnail delivered as bytes inside JSON | `Image.memory` |

  • Why does Image.network download a listing photo again after the app restarts?
    `NetworkImage` keeps only the decoded image in the in-memory `ImageCache`; it never writes the downloaded bytes to disk. When the process ends, or the cache is cleared under memory pressure, the image is gone and the next display fetches it again. Persisting photos across launches needs a separate disk-caching layer.
  • What happens to the cache entry when Image.network gets a 404?
    `NetworkImage` throws a `NetworkImageLoadException` carrying the status code, and it evicts the key from the `ImageCache`, so a later attempt, such as a retry button rebuilding the image, goes back to the network instead of replaying the cached failure. Without an `errorBuilder`, the error is reported to `FlutterError.onError`.

saying these in an interview costs you the question

  • Image.network caches downloaded photos on disk between app launches.
  • Image.file works on the web because browsers expose a file system.
  • Only Image.network supports cacheWidth and cacheHeight.
  • Each Image widget decodes its own copy even when the URL is the same.
  • A failed network image stays cached, so retries never hit the server.
open as a page

In Flutter, what do cacheWidth and cacheHeight do on Image.network, and why can they stop a photo gallery from running out of memory?

level: middleimportance: must knowfreq 55%

basics

~20 s

They make the engine decode the image at the given pixel size instead of full resolution. Decoded images cost width × height × 4 bytes, so a 4000 × 3000 photo takes about 48 MB however small its JPEG; decoding at thumbnail size cuts that sharply.

open as a page

In Flutter, how do an Image's loadingBuilder, frameBuilder and errorBuilder differ, and how would you use them for a listing photo?

level: middleimportance: should knowfreq 44%

basics

~20 s

loadingBuilder reports download progress and is called with null once loading completes; frameBuilder learns when the first frame is ready, for placeholders and fade-ins; errorBuilder replaces the image when loading fails. When both are set, loadingBuilder's child is frameBuilder's result.

open as a page

In a Flutter photo viewer, how do precacheImage and gaplessPlayback each prevent a blank flash when the user swipes to the next listing photo?

level: middleimportance: should knowfreq 33%

basics

~20 s

precacheImage decodes the next photo into the ImageCache before it is shown, so the swipe finds it ready. gaplessPlayback, false by default, keeps an Image showing its old picture while a changed provider loads, instead of going blank.

open as a page

A Flutter real-estate gallery of large remote photos crashes with out-of-memory on older phones; how does ImageCache behave, and what would you change?

level: seniorimportance: should knowfreq 40%

basics

~20 s

ImageCache keeps up to 1000 decoded images or 100 MiB, but images still on screen are live and not counted. Such crashes usually come from decoding full-resolution photos, so decode smaller with cacheWidth first, then tune the limits.

open as a page

In Flutter, how does an ImageProvider's cache key decide whether two images share one decoded copy, and where do keys go wrong?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

ImageCache reuses a decoded image whenever two providers produce equal keys from obtainKey. NetworkImage compares URL, scale and headers; FileImage only the path; MemoryImage the exact Uint8List instance, which is where most key bugs start.

open as a page