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. 1.Always provide errorBuilder for graceful degradation:
  2. 2.```dart
  3. 3.Image.network(
  4. 4.'https://api.example.com/images/user-avatar.jpg',
  5. 5.errorBuilder: (context, error, stackTrace) {
  6. 6.return Container(
  7. 7.width: 100,
  8. 8.height: 100,
  9. 9.color: Colors.grey[300],
  10. 10.child: const Icon(Icons.error_outline, size: 40),
  11. 11.);
  12. 12.},
  13. 13.loadingBuilder: (context, child, loadingProgress) {
  14. 14.if (loadingProgress == null) return child;
  15. 15.return const CircularProgressIndicator();
  16. 16.},
  17. 17.);
  18. 18.`
  19. 19.For self-signed certificates in development only, configure HttpClient:
  20. 20.```dart
  21. 21.class DevHttpOverrides extends HttpOverrides {
  22. 22.@override
  23. 23.HttpClient createHttpClient(SecurityContext? context) {
  24. 24.return super.createHttpClient(context)
  25. 25...badCertificateCallback =
  26. 26.(X509Certificate cert, String host, int port) => true;
  27. 27.}
  28. 28.}

// In main(): void main() { HttpOverrides.global = DevHttpOverrides(); runApp(const MyApp()); } ```

  1. 1.For authenticated image requests, use a custom HttpClient:
  2. 2.```dart
  3. 3.Image.network(
  4. 4.'https://api.example.com/private/image.jpg',
  5. 5.headers: {'Authorization': 'Bearer $authToken'},
  6. 6.);
  7. 7.`
  8. 8.For Flutter web CORS issues, ensure the server sends proper headers:
  9. 9.`
  10. 10.Access-Control-Allow-Origin: *
  11. 11.Access-Control-Allow-Methods: GET, OPTIONS
  12. 12.`
  13. 13.Or use a proxy in development:
  14. 14.```dart
  15. 15.// During development only
  16. 16.final imageUrl = kIsWeb
  17. 17.? 'https://cors-anywhere.herokuapp.com/$imageUrl'
  18. 18.: imageUrl;
  19. 19.`
  20. 20.Pre-fetch and cache images with cached_network_image:
  21. 21.```yaml
  22. 22.dependencies:
  23. 23.cached_network_image: ^3.3.1
  24. 24.`
  25. 25.```dart
  26. 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. 1.Validate URLs before passing to Image.network:
  2. 2.```dart
  3. 3.Uri? parseImageUrl(String? url) {
  4. 4.if (url == null || url.isEmpty) return null;
  5. 5.final uri = Uri.tryParse(url);
  6. 6.if (uri == null || !uri.hasScheme || !uri.hasAuthority) return null;
  7. 7.return uri;
  8. 8.}
  9. 9.`

Prevention

  • Always use errorBuilder with Image.network
  • Use cached_network_image package for production apps
  • Validate URLs before rendering
  • Set memCacheWidth and memCacheHeight to reduce memory usage
  • Use a loading indicator for better UX during image fetch
  • Never disable certificate validation in production builds