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).