Reputation: 1073
How to handle Image.network
when the url is wrong or the destination leads to 404.
for example try
Image.network('https://image.tmdb.org/t/p/w92')
Upvotes: 87
Views: 139762
Reputation: 2926
I was getting error in the hightest voted answer, StackTrace is nullable. So I used like this:
Image.network(
'image url',
errorBuilder: (BuildContext context,
Object exception,
StackTrace? stackTrace) {
return const Icon(Icons.error);
},
),
Upvotes: 0
Reputation: 6413
I'd prefer using Icon like that:
Image.network(
imageUrl,
errorBuilder: (context, error, stackTrace) => const Icon(Icons.error),
)
Upvotes: 0
Reputation: 11
You can use the following code to change how the dart VM handles the error. Provide the exception library string and the status code which can be any between 400-404. Such cases will just print the exception to the console in case of debug mode and won't be reported as a crash incase you have Firebase Crashlytics installed.
FlutterError.onError = (FlutterErrorDetails details) {
if (details.library == 'image resource service' &&
details.exception.toString().contains('400')) {
debugPrint('Suppressed cachedNetworkImage Exception');
return;
}
FlutterError.presentError(details);
};
PS: Add this code in the main() function after WidgetsFlutterBinding.ensuredInitialized()
Upvotes: 0
Reputation: 516
I personally don't like to rely on 3rd party packages except utterly necessary.. however, my approach of handling network image error is as below:
FadeInImage(
imageErrorBuilder: (context, error, stackTrace) {
return Image.asset(
"assets/placeholder.png",
fit: BoxFit.contain,
width: 150,
);
},
height: 100,
fit: BoxFit.cover,
placeholder: const AssetImage("assets/placeholder.png"),
image: Image.network(
getImageURL(selectedUser.profilePic!),
).image,
),
as you can see, wrapping with fadeInImage gives us a nice trasition and at the same time, the errorBuilder catches the exception and returns a placeholder image... you can return an error image as well.. but the idea reamains the same
Upvotes: -1
Reputation: 4507
UPDATE: Look at the new accurate way which uses an inbuilt method, answered by @Niraj Phutane here which requires no plugins.
I recommend using cached_network_image which gives an option to add a placeholder image and also an error widget in case of a 404 or 403.
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => new CircularProgressIndicator(),
errorWidget: (context, url, error) => new Icon(Icons.error),
),
Edit: If your app crashes when url is a 404, the faq has an entry regarding this, please file a bug report with a reproducible example to cached_network_image on github.
Upvotes: 24
Reputation: 21
there what i was looking for, but its temporary... its ignore 404 error exception and returning null.
FlutterError.onError = (FlutterErrorDetails details) {
if (details.library == 'image resource service') {
return;
}
FirebaseCrashlytics.instance.recordFlutterFatalError(details);
};
for more information errorBuilder throws exception for invalid image data #107416
Upvotes: 2
Reputation: 111
I ran into this problem while learning flutter (not web). I found the solution after I tried all the non-working options that are given in this thread. They all print errors to the console if the address is wrong or the resource is not found. To play with my solution, you will need to install two plugins: http and url_launcher (the second plugin is only needed to open a link to this topic in the simulator). Be sure to read the up-to-date instructions for using plugins. How to play, you ask? Change the address in input Image URL field as you like! Please report bugs if you find them.
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image 404',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const _problemDescriptionUrl =
'https://stackoverflow.com/questions/52568872/flutter-how-to-handle-image-network-error-like-404-or-wrong-url';
String imageUrl =
'https://repository-images.githubusercontent.com/90528830/018a6400-d879-11e9-92b4-492c61d9bc32';
late Future<Widget> futureWidget;
Future<Widget> fetchImage() async {
if (imageUrl.startsWith('https://') &&
Uri.tryParse(imageUrl) != null &&
Uri.tryParse(imageUrl)!.hasAbsolutePath) {
final response = await http.get(Uri.parse(imageUrl));
if (response.statusCode == 200 &&
(response.headers.keys.contains('content-type') &&
!response.headers['content-type']!.contains('text/html'))) {
// If the server did return a 200 OK response,
// then parse the data.
return Image.memory(response.bodyBytes, fit: BoxFit.cover);
} else {
// If the server did not return a 200 OK response,
// then
return const Text('Failed to load Image');
}
}
return const Text('Failed to load Image');
}
@override
void initState() {
futureWidget = fetchImage();
super.initState();
}
void _launchUrl() async {
if (!await launchUrl(Uri.tryParse(_problemDescriptionUrl)!)) {
throw 'Could not launch $_problemDescriptionUrl';
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Image.network Error 404 Solution'),
),
body: Column(
children: [
TextButton(
onPressed: _launchUrl,
child: const Text('The problem described here')),
Container(
padding: const EdgeInsets.all(8),
alignment: Alignment.center,
width: double.infinity,
height: 200,
child: FutureBuilder<Widget>(
future: futureWidget,
builder: (context, snapshot) {
if (snapshot.hasData) {
return snapshot.data!;
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
),
Form(
child: TextFormField(
decoration: const InputDecoration(labelText: 'Image URL'),
initialValue: imageUrl,
maxLines: 3,
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
onChanged: (val) {
setState(() {
imageUrl = val;
futureWidget = fetchImage();
});
},
),
)
],
),
);
}
}
Upvotes: 2
Reputation: 101
Can use errorBuilder from Image.network or Image.asset
Image.network(
path.image,
width: 40,
height: 40,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
return const Text('😢');
},
Upvotes: 9
Reputation: 19514
You can use ImageStream
to handle errors.
class CustomProductImage extends StatefulWidget {
final String image;
const CustomProductImage(this.image);
@override
State<CustomProductImage> createState() => _CustomProductImageState();
}
class _CustomProductImageState extends State<CustomProductImage> {
Widget mainWidget = const CircularProgressIndicator(); // Placeholder widget
@override
void initState() {
super.initState();
Image image = Image.network(widget.image, width: 50, height: 50);
final ImageStream stream = image.image.resolve(ImageConfiguration.empty);
stream.addListener(ImageStreamListener((info, call) {
if (!completer) {
completer = true;
setState(() {
mainWidget = image;
});
}
}, onError: (dynamic exception, StackTrace? stackTrace) {
print('Error is $exception , stack is $stackTrace');
setState(() {
mainWidget = const Icon(Icons.error); // Error Widget
});
}));
}
@override
Widget build(BuildContext context) {
return mainWidget;
}
}
Upvotes: 6
Reputation: 4007
For those who are using firebase storage
The above methods didn't help me at all.
When the object is not found in firebase, I got an exception "Null is not a type of List<int>"
.
So I decided to verify if the Unit8List values are valid or not.
If it's valid then we are ready to show the image otherwise use any widgets as placeholder.
Future<String?> getImgUrl()async{
//Storage structre: avatars/+919999999999/avatar.jpg
//Permanent url of an image without tokens
//%2F means "/"
//%2B mans "+"
String imgUrl = "https://firebasestorage.googleapis.com/v0/b/yourFIrebaseProjectID/o/avatars%2F%2B919999999999%2Favatar.jpg?alt=media";
try {
Uint8List bytes = (await NetworkAssetBundle(Uri.parse(imgUrl)).load(imgUrl)).buffer.asUint8List();
print("The image exists!");
return imgUrl;
} catch (e) {
print("Error: $e");
return null;
}
}
Widget futureBulder(){
return FutureBuilder(
future: getImgUrl(),
builder: (BuildContext context, AsyncSnapshot<String?> snapshot) {
bool error = snapshot.data==null;
//I use NetworkImage for demonstration purpose, use it whatever widget u want
return CircleAvatar(
backgroundImage: error? null : NetworkImage(snapshot.data!),
);
},
);
}
Upvotes: 2
Reputation: 343
I used the basic Image widget and added an error builder for errors and a loadingBuilder for loading status and for the actual image I used NetworkImageWithRetry so that it will try to load the image many times and give you an error at the end if it couldn't load the image.
Image(
image: NetworkImageWithRetry(
'http://ImageUrl.png',),
errorBuilder: (context, exception, stackTrack) => Icon(Icons.error,),
loadingBuilder: (context, exception, stackTrack) => CircularProgressIndicator(),
)
Upvotes: 2
Reputation: 398
CachedNetworkImage(
placeholder: (context, url) =>
Center(child: new CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Icon(Icons.error),
imageUrl: image,
fit: BoxFit.fill,
)
Upvotes: 0
Reputation: 7105
https://pub.dev/packages/extended_image
the plugin makes it easy to handle errors in Network image load
import 'package:flutter/material.dart';
import 'package:extended_image/extended_image.dart';
class ImageHolder extends StatefulWidget {
const ImageHolder(
{Key? key,
required this.url,
required this.imageHeight,
required this.imageWidth})
: super(key: key);
final String url;
final double imageHeight;
final double imageWidth;
@override
_ImageHolderState createState() => _ImageHolderState();
}
class _ImageHolderState extends State<ImageHolder>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
lowerBound: 0.0,
upperBound: 1.0);
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ExtendedImage.network(
widget.url,
fit: BoxFit.contain,
cache: true,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
_controller.reset();
return Image.asset(
'assets/images/loading.gif',
fit: BoxFit.fill,
);
case LoadState.completed:
_controller.forward();
return FadeTransition(
opacity: _controller,
child: ExtendedRawImage(
image: state.extendedImageInfo?.image,
width: widget.imageWidth,
height: widget.imageHeight,
),
);
case LoadState.failed:
_controller.reset();
state.imageProvider.evict();
return Image.asset(
'assets/images/failed.jpg',
);
}
},
);
}
}
Upvotes: 0
Reputation: 624
I have implemented this StatefulWidget for the same case, hope this helps!!!
class NetworkImageHandlerViewer extends StatefulWidget {
final String imageURL;
bool errorFoundInImageLoad = false;
NetworkImageHandlerViewer({
Key key,
@required this.imageURL,
}) : super(key: key);
@override
_NetworkImageHandlerViewerState createState() =>
_NetworkImageHandlerViewerState();
}
class _NetworkImageHandlerViewerState extends State<NetworkImageHandlerViewer> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
height: 200,
// height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
// color: Colors.black,
child: (widget.errorFoundInImageLoad)
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Unable To Fetch Image",
),
IconButton(
iconSize: 40,
onPressed: () {
setState(() {
if (mounted) {
print("reloading image again");
setState(() {
widget.errorFoundInImageLoad = false;
});
}
});
},
icon: Icon(Icons.refresh),
),
Text(
"Tap Refresh Icon To Reload!!!",
),
],
)
: Image.network(
// widget.question.fileInfo[0].remoteURL,
widget.imageURL,
//
loadingBuilder: (context, child, loadingProgress) =>
(loadingProgress == null)
? child
: Center(
child: CircularProgressIndicator(),
),
errorBuilder: (context, error, stackTrace) {
Future.delayed(
Duration(milliseconds: 0),
() {
if (mounted) {
setState(() {
widget.errorFoundInImageLoad = true;
});
}
},
);
return SizedBox.shrink();
},
),
),
SizedBox(height: 25),
],
);
}
}
Upvotes: 0
Reputation: 62419
You can display image from assets when you found 404 or any other error while loading image.
What I have done is:
FadeInImage(
image: NetworkImage("imageUrl"),
placeholder: AssetImage(
"assets/images/placeholder.jpg"),
imageErrorBuilder:
(context, error, stackTrace) {
return Image.asset(
'assets/images/error.jpg',
fit: BoxFit.fitWidth);
},
fit: BoxFit.fitWidth,
)
Check imageErrorBuilder
property.
Upvotes: 16
Reputation: 1926
I have handled the network image issue related to 404 by using an errorBuilder.
Image.network('Your image url...',
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
return Text('Your error widget...');
},
),
Upvotes: 135
Reputation: 89
I had the same problem, but, I solved it using the property 'imageErrorBuilder' of the FadeInImage class.
Here is a link about that: https://api.flutter.dev/flutter/widgets/Image/errorBuilder.html
The example was made for Image.network(), but it also work for FadeInImage.
Here's my code:
FadeInImage(
imageErrorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
print('Error Handler');
return Container(
width: 100.0,
height: 100.0,
child: Image.asset('assets/img/no_disponible.jpg'),
);
},
placeholder: AssetImage('assets/img/loading.gif'),
image: NetworkImage(product.getImg()),
fit: BoxFit.cover,
height: 100.0,
width: 100.0,
),
The function of imageErrorBuilder will be execute for example, if the direction of the page doesn't exist.
I'm using Flutter 1.20.1
Upvotes: 2
Reputation: 71
It's weird but you can't easily handle errors correctly with Image.Provider. Use this handy package: https://pub.dev/packages/flutter_advanced_networkimage
Then in you code:
backgroundImage: AdvancedNetworkImage(
"YOUR IMAGE URL",
fallbackAssetImage: "YOUR DEAULT ASSERT IMAGE eg:assets/default.png"
)
Upvotes: 3
Reputation: 1073
Instead of Network.image
use NetworkImageWithRetry
https://pub.dartlang.org/documentation/flutter_image/latest/
Example:
var avatar = Image(
image: NetworkImageWithRetry('http://example.com/avatars/123.jpg'),
);
Upvotes: 10