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 one rror 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)
CodePudding user response:
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,
),
),
),
);
}
}
CodePudding user response:
I think you should use the Image.network
class replacing your NetworkImage
.
It contains especially an errorBuilder
parameter that allows you to set a custom widget in case of error :
Image.network(
String src,
{Key? key,
double scale = 1.0,
ImageFrameBuilder? frameBuilder,
ImageLoadingBuilder? loadingBuilder,
ImageErrorWidgetBuilder? errorBuilder,
...
int? cacheWidth,
int? cacheHeight}
)
If you need the ImageProvider
, then your method can look like this :
ImageProvider<Object> getCharacterAvatar(String url) {
final image = Image.network(url, errorBuilder: (context, object, trace) {
return Image(image: AssetImage('assets/images/village-not-found-logo.png'));
},).image;
return image;
}
Please note the .image
at the end of the Image.network().image
to return the ImageProvider
.