Introduction
Image.network is the standard way to load remote images in Flutter, but it can fail with HTTP errors when the server returns a non-200 status, when there are CORS restrictions, when SSL certificates are invalid, or when the device has no network connectivity. Proper error handling is essential for a good user experience.
Symptoms
- Console error:
HTTP request failed, statusCode: 404, https://example.com/image.jpg - Or:
HandshakeException: Handshake error in client - Or:
SocketException: Failed host lookup - Image shows as broken/empty on screen
- Works on web but fails on mobile (or vice versa)
Common Causes
- Image URL returns 404 or other HTTP error status
- Self-signed or expired SSL certificate on the image server
- CORS policy blocking requests on Flutter web
- No internet connectivity on device
- Image server requires authentication headers
- Content-Type response header is not an image type
Step-by-Step Fix
- 1.Always provide errorBuilder for graceful degradation:
- 2.```dart
- 3.Image.network(
- 4.'https://api.example.com/images/user-avatar.jpg',
- 5.errorBuilder: (context, error, stackTrace) {
- 6.return Container(
- 7.width: 100,
- 8.height: 100,
- 9.color: Colors.grey[300],
- 10.child: const Icon(Icons.error_outline, size: 40),
- 11.);
- 12.},
- 13.loadingBuilder: (context, child, loadingProgress) {
- 14.if (loadingProgress == null) return child;
- 15.return const CircularProgressIndicator();
- 16.},
- 17.);
- 18.
` - 19.For self-signed certificates in development only, configure HttpClient:
- 20.```dart
- 21.class DevHttpOverrides extends HttpOverrides {
- 22.@override
- 23.HttpClient createHttpClient(SecurityContext? context) {
- 24.return super.createHttpClient(context)
- 25...badCertificateCallback =
- 26.(X509Certificate cert, String host, int port) => true;
- 27.}
- 28.}
// In main(): void main() { HttpOverrides.global = DevHttpOverrides(); runApp(const MyApp()); } ```
- 1.For authenticated image requests, use a custom HttpClient:
- 2.```dart
- 3.Image.network(
- 4.'https://api.example.com/private/image.jpg',
- 5.headers: {'Authorization': 'Bearer $authToken'},
- 6.);
- 7.
` - 8.For Flutter web CORS issues, ensure the server sends proper headers:
- 9.
` - 10.Access-Control-Allow-Origin: *
- 11.Access-Control-Allow-Methods: GET, OPTIONS
- 12.
` - 13.Or use a proxy in development:
- 14.```dart
- 15.// During development only
- 16.final imageUrl = kIsWeb
- 17.? 'https://cors-anywhere.herokuapp.com/$imageUrl'
- 18.: imageUrl;
- 19.
` - 20.Pre-fetch and cache images with cached_network_image:
- 21.```yaml
- 22.dependencies:
- 23.cached_network_image: ^3.3.1
- 24.
` - 25.```dart
- 26.import 'package:cached_network_image/cached_network_image.dart';
CachedNetworkImage( imageUrl: 'https://api.example.com/images/photo.jpg', placeholder: (context, url) => const CircularProgressIndicator(), errorWidget: (context, url, error) => const Icon(Icons.broken_image), memCacheWidth: 400, // Reduce memory usage memCacheHeight: 400, ); ```
- 1.Validate URLs before passing to Image.network:
- 2.```dart
- 3.Uri? parseImageUrl(String? url) {
- 4.if (url == null || url.isEmpty) return null;
- 5.final uri = Uri.tryParse(url);
- 6.if (uri == null || !uri.hasScheme || !uri.hasAuthority) return null;
- 7.return uri;
- 8.}
- 9.
`
Prevention
- Always use
errorBuilderwithImage.network - Use
cached_network_imagepackage for production apps - Validate URLs before rendering
- Set
memCacheWidthandmemCacheHeightto reduce memory usage - Use a loading indicator for better UX during image fetch
- Never disable certificate validation in production builds