mohamed zayani
mohamed zayani

Reputation: 1073

Flutter how to handle Image.network error (like 404 or wrong url)

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

Answers (19)

Mohammed Ali
Mohammed Ali

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

Adam Smaka
Adam Smaka

Reputation: 6413

I'd prefer using Icon like that:

Image.network(
          imageUrl,
          errorBuilder: (context, error, stackTrace) => const Icon(Icons.error),
        )

Upvotes: 0

Abdullah Ihsan
Abdullah Ihsan

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

androCoder-BD
androCoder-BD

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

Phani Rithvij
Phani Rithvij

Reputation: 4507

UPDATE: Look at the new accurate way which uses an inbuilt method, answered by @Niraj Phutane here which requires no plugins.


old answer (before errorbuilder existed)

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

Dimas Ibnu Malik
Dimas Ibnu Malik

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

Alexander Kokin
Alexander Kokin

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

luis tundisi
luis tundisi

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

BIS Tech
BIS Tech

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

Rajesh
Rajesh

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

Khaled Mahmoud
Khaled Mahmoud

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

Reshnu chandran
Reshnu chandran

Reputation: 398

CachedNetworkImage(
  placeholder: (context, url) =>
               Center(child: new CircularProgressIndicator()),
  errorWidget: (context, url, error) => 
               Icon(Icons.error),
  imageUrl: image,
  fit: BoxFit.fill,
              )

Upvotes: 0

Javeed Ishaq
Javeed Ishaq

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

Mrudul Addipalli
Mrudul Addipalli

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

Pratik Butani
Pratik Butani

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

NirajPhutane
NirajPhutane

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...');
    },
),

errorBuilder property

Upvotes: 135

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

Harold Lomotey
Harold Lomotey

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

mohamed zayani
mohamed zayani

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

Related Questions