[SOLVED] Flutter: How to handle NetworkImage with Invalid Image Data exception?

Question

This Content is from Stack Overflow. Question asked by Shlema Goldsmith

I am trying to use NetworkImage as the image in a BoxDecoration for a Container. The url passed to the NetworkImage will sometimes hold a bad image type (the url works and is correct but the actual image at the specified url is bad) resulting in an error.

To handle this occurrence I set up a method that uses a try-catch block where it returns the NetworkImage if successful, and a preset AssetImage in the event of an error. The try-catch block is not handling this exception, and throws an error instead of returning the AssetImage specified in the catch.

I’ve seen that Image.network has an onError parameter which looks like it would solve the problem, but Image.network is of type “Image” and BoxDecoration requires an “ImageProvider” (NetworkImage, AssetImage), so that does not help in this case.

Which is the best way to handle this error so that I can show an AssetImage in the case of the NetworkImage throwing an error?

Here is the Widget holding the BoxDecoration where I call the method I created to handle fetching the NetworkImage:

class CharacterPreviewCard extends StatelessWidget {
  final CharacterPreview character;
  const CharacterPreviewCard({Key? key, required this.character})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        context.router
            .push(CharacterDetailsRoute(characterId: character.characterId));
      },
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            height: 171,
            width: 171,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(16),
              color: Colors.black,
              image: DecorationImage(
                image: getCharacterAvatar(character.characterAvatarUrl),
                fit: BoxFit.fill,
              ),
            ),
          ),
          const SizedBox(height: smallMargin),
          Padding(
            padding: const EdgeInsets.only(left: smallMargin),
            child: SizedBox(
              width: 165,
              child: Text(
                character.characterName,
                style: Theme.of(context).textTheme.bodyLarge,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              ),
            ),
          ),
        ],
      ),
    );
  }

Here is the method “getCharacterAvatar” which should return either NetworkImage or AssetImage:

ImageProvider<Object> getCharacterAvatar(String url) {
    try {
      final image = NetworkImage(url);
      return image;
    } catch (e) {
      return const AssetImage('assets/images/village-not-found-logo.png');
    }
  }

And here is the error in Debug Console:

The following _Exception was thrown resolving an image codec:
Exception: Invalid image data

When the exception was thrown, this was the stack
#0      _futurize (dart:ui/painting.dart:5718:5)
#1      ImageDescriptor.encoded (dart:ui/painting.dart:5574:12)
#2      instantiateImageCodec (dart:ui/painting.dart:2056:60)
<asynchronous suspension>
Image provider: NetworkImage("https://narutoql.s3.amazonaws.com/Hana.jpg", scale: 1.0)
Image key: NetworkImage("https://narutoql.s3.amazonaws.com/Hana.jpg", scale: 1.0)

Solution

Currently, there is no way to catch errors with NetworkImage or Image.network. More on this can be found here: https://github.com/flutter/flutter/issues/20910.

Thanks to Tom3652’s comment suggesting the use of CachedNetworkImage I was able to find a solution that works using the Widget CachedNetworkImage with an errorWidget parameter to display an AssetImage when an error was thrown.

I replaced the Container that had the NetworkImage as a parameter for DecorationImage with a custom widget (to minimized code in the file). The custom widget returns the CachedNetworkImage.

Here is the solution that worked for me:

class PreviewCardImage extends StatelessWidget {
  final String url;
  final AssetImage errorImage;
  const PreviewCardImage({
    Key? key,
    required this.url,
    required this.errorImage,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      imageUrl: url,
      imageBuilder: (context, imageProvider) => Container(
        height: 171,
        width: 171,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(16),
          color: Colors.black,
          image: DecorationImage(
            image: imageProvider,
            fit: BoxFit.fill,
          ),
        ),
      ),
      placeholder: (context, url) => const CircularProgressIndicator(),
      errorWidget: (context, url, error) => Container(
        height: 171,
        width: 171,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(16),
          color: Colors.black,
          image: DecorationImage(
            image: errorImage,
            fit: BoxFit.fill,
          ),
        ),
      ),
    );
  }
}

Answered by Shlema Goldsmith


This Question and Answer are collected from stackoverflow and tested by JTuto community, is licensed under the terms of CC BY-SA 4.0.

people found this article helpful. What about you?