Search code examples
flutterflutter-image

Cannot catch NetworkImageLoadException when Image.network() failed


I use Image.network() to show image from URL this is how I use it

Image image = Image.network(
      _auth.currentUser!.photoURL!,
      width: 100.getWidth(context),
      height: 100.getWidth(context),
      frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
        return wasSynchronouslyLoaded
            ? child
            : _profileImagePlaceholder(context);
      },
      loadingBuilder: (context, child, loadingProgress) {
        return loadingProgress == null
            ? child
            : _profileImagePlaceholder(context);
      },
      errorBuilder: (context, error, stackTrace) {
        return _profileImagePlaceholder(context);
      },
    );

But even when I set errorBuilder or even wrap the whole thing with try/catch This NetworkImageLoadException still show

Full Exception

The following NetworkImageLoadException was thrown resolving an image codec:
HTTP request failed, statusCode: 403,


When the exception was thrown, this was the stack:
#0      NetworkImage._loadAsync (package:flutter/src/painting/_network_image_io.dart:99:9)
<asynchronous suspension>
...

Image provider:
  NetworkImage("https://firebasestorage.googleapis.com/v0/b/biddee-co.appspot.com/o/profiles%2FdefaultProfile.png?alt=media&token=a4a99031-aabd-4597-b075-77ecb2d3e594",
  scale: 1.0)
Image key:
  NetworkImage("https://firebasestorage.googleapis.com/v0/b/biddee-co.appspot.com/o/profiles%2FdefaultProfile.png?alt=media&token=a4a99031-aabd-4597-b075-77ecb2d3e594",
  scale: 1.0)

Solution

  • In my case, what fixed it in production code was to add a listener in the image resolve with a onError:

    final image = Image.network(
      // Properties.
      errorBuilder: // ...,
    );
    image.image.resolve(ImageConfiguration.empty).addListener(
          ImageStreamListener(
            (_, __) { /* You can do something when the image is loaded. */ },
            onError: (_, __) {
              // Evict the object from the cache to retry to fetch it the next
              // time this widget is built.
              imageCache.evict(image.image);
            },
          ),
        );
    

    While this works fine in debug / prod, in the tests where I used HttpOverrides.runZoned() with a client that always returns 404, the tests were always failing with an uncaught NetworkImageLoadException.

    The fix was to listen to FlutterError at the beginning of the test and only throw if the exception caught was not a NetworkImageLoadException.

    WidgetsFlutterBinding.ensureInitialized();
    FlutterError.onError = (details) {
      if (details.exception is! NetworkImageLoadException) throw details.exception;
    };