Reader Stacks

Displaying Images in Flutter: Local Assets, Network Images, and Backgrounds

A local asset image needs registering in pubspec.yaml before Image.asset() can find it — the single most common reason a bundled image silently fails to load in a new Flutter project.

Displaying Images in Flutter: Local Assets, Network Images, and Backgrounds

Flutter has three genuinely distinct ways to display an image — bundled with the app, loaded from a network URL, and as a full-screen or container background — each with its own setup requirements.

Displaying a local (bundled) image asset

# pubspec.yaml
flutter:
  assets:
    - assets/images/logo.png
Image.asset('assets/images/logo.png', width: 150)

Registering the asset path in pubspec.yaml is a required step, not optional — an image file placed in the project without also being listed here will silently fail to load, one of the most common early mistakes in a new Flutter project.

Displaying a network image

Image.network(
  'https://example.com/photo.jpg',
  width: 200,
  fit: BoxFit.cover,
)

Showing a loading indicator while a network image loads

Image.network(
  'https://example.com/photo.jpg',
  loadingBuilder: (context, child, progress) {
    if (progress == null) return child;
    return const Center(child: CircularProgressIndicator());
  },
  errorBuilder: (context, error, stackTrace) {
    return const Icon(Icons.broken_image);
  },
)

loadingBuilder and errorBuilder handle the two states a network image inherently has that a bundled local asset doesn't — a period while it's still downloading, and the possibility it fails to load at all (a bad URL, no connectivity) — both worth handling explicitly rather than leaving a blank space.

Setting a full-screen background image

Scaffold(
  body: Container(
    decoration: const BoxDecoration(
      image: DecorationImage(
        image: AssetImage('assets/images/background.jpg'),
        fit: BoxFit.cover,
      ),
    ),
    child: const Center(child: Text('Content over the background')),
  ),
)

DecorationImage inside a Container's BoxDecoration is the standard way to set a background image — unlike a plain Image widget, it's specifically designed to sit behind other child content rather than being the only thing displayed.

Controlling how an image fills its available space

Image.network(url, fit: BoxFit.cover);   // fills the space, cropping if needed
Image.network(url, fit: BoxFit.contain); // fits entirely within, may show empty space
Image.network(url, fit: BoxFit.fill);    // stretches to fill, may distort aspect ratio

BoxFit.cover is the most common choice for a photo that should fill its container attractively (like a card thumbnail or background), since it crops rather than distorts — BoxFit.contain suits a logo or icon that must never be cropped, even if that leaves empty space around it.

Caching a network image to avoid re-downloading it repeatedly

// pubspec.yaml: cached_network_image: ^3.3.0
CachedNetworkImage(
  imageUrl: 'https://example.com/photo.jpg',
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
)

Plain Image.network() re-fetches the image from the network on every rebuild unless Flutter's own image cache happens to still hold it — the cached_network_image package adds more explicit, reliable disk-backed caching, worth adding for any app displaying the same network images repeatedly (like avatars in a list).