【问题标题】:Flutter how to handle Image.network error (like 404 or wrong url)Flutter 如何处理 Image.network 错误(如 404 或错误的 url)
【发布时间】:2019-03-05 06:00:55
【问题描述】:

Image.networkurl错误或目的地导致404时如何处理。

例如尝试

Image.network('https://image.tmdb.org/t/p/w92')

【问题讨论】:

标签: flutter http-status-code-404


【解决方案1】:

我已经使用 errorBuilder 处理了与 404 相关的网络图像问题。

Image.network('Your image url...',
    errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
        return Text('Your error widget...');
    },
),

errorBuilder property

【讨论】:

  • 太棒了,就像一个魅力,最优雅的解决方案
  • 漂亮!这个错误生成器是最近在 2020 年初添加的。在我不得不使用“precacheImage”检查 url 是否有效之前,这确实使代码变得复杂。
  • 工作正常。谢谢
  • 如果用户在加载图像之前返回屏幕,我会收到异常错误。仅在 URL 返回 404 的情况下才会发生,否则一切正常。如何预防?
  • 在firebase存储中找不到对象时不起作用。
【解决方案2】:

更新:查看使用内置方法的新精确方法,由@Niraj Phutane here 回答,不需要插件。


过时的旧答案(在 errorbuilder 存在之前)

我建议使用cached_network_image,它提供了添加占位符图像的选项以及在 404 或 403 的情况下添加错误小部件。

CachedNetworkImage(
   imageUrl: "http://via.placeholder.com/350x150",
   placeholder: (context, url) => new CircularProgressIndicator(),
   errorWidget: (context, url, error) => new Icon(Icons.error),
),

【讨论】:

  • CachedNetworkImage 导致应用在状态码为 404 时冻结
  • CachedNetworkImage 在 404 时仍然抛出异常
  • @Jorge 你找到 404 的解决方案了吗?
  • 如果用户在加载图像之前返回屏幕,我会收到异常错误。仅在 URL 返回 404 的情况下才会发生,否则一切正常。如何预防?
【解决方案3】:

NetworkImageWithRetry代替Network.image

https://pub.dartlang.org/documentation/flutter_image/latest/

例子:

var avatar = Image(
  image: NetworkImageWithRetry('http://example.com/avatars/123.jpg'),
);

【讨论】:

  • 你不需要使用以 Dart 2.0 开头的new 关键字
  • 遇到 404 错误时NetworkImageWithRetry 会做什么?
  • @krupeshAnadkat 将flutter_image: ^1.0.0 添加到您的pubspek.yaml 文件中
  • @krupesh-anadkat 还添加了导入 import 'package:flutter_image/network.dart';
  • @JenJose 为此,您考虑过 AutomaticKeepAliveClientMixin 吗?您将它添加到您的包装类并保持状态。或者,如果它是一个 ListView,您可以将其配置为保持状态:但如果您真的需要使用图像缓存(即 precacheImage),我相信有一种方法可以将 precacheImage 与字节加载相结合。我正在考虑就这个问题提出一个问题。
【解决方案4】:

当您在加载图像时发现 404 或任何其他错误时,您可以显示资产中的图像。

我所做的是:

  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,
    )

检查imageErrorBuilder 属性。

【讨论】:

  • 抛出 404 时不起作用
  • 对我来说看起来很棒。
【解决方案5】:

您可以使用ImageStream 来处理错误。

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;
  }
}

【讨论】:

【解决方案6】:

这很奇怪,但您不能轻松地使用 Image.Provider 正确处理错误。 使用这个方便的包:https://pub.dev/packages/flutter_advanced_networkimage

然后在你的代码中:

backgroundImage: AdvancedNetworkImage(
                    "YOUR IMAGE URL", 
                    fallbackAssetImage: "YOUR DEAULT ASSERT IMAGE eg:assets/default.png"
                  )

【讨论】:

  • 这是这里唯一没有抛出错误并挂起我的应用程序的解决方案。但是在 GitHub 中,这个包被标记为已归档。有谁知道为什么?它也无法在 Flutter v1.2.x+ 上构建
  • 很遗憾我遇到了这个错误:github.com/dnfield/flutter_svg/issues/368 在尝试与 Flutter 1.20.2 一起使用时。我仍然不知道如何捕捉加载网络图像期间发生的所有错误。 :-(
【解决方案7】:

我遇到了同样的问题,但是,我使用 FadeInImage 类的属性“imageErrorBuilder”解决了它。

这里有一个链接:https://api.flutter.dev/flutter/widgets/Image/errorBuilder.html

该示例是为 Image.network() 制作的,但它也适用于 FadeInImage。

这是我的代码:

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,
), 

如果页面方向不存在,则会执行imageErrorBuilder函数。

我正在使用 Flutter 1.20.1

【讨论】:

  • 这对我有用。谢谢。
  • 404 时抛出异常!
【解决方案8】:

可以使用 Image.network 或 Image.asset 中的 errorBuilder

Image.network(
       path.image,
       width: 40,
       height: 40,
       errorBuilder: (BuildContext context, Object exception,
            StackTrace? stackTrace) {
         return const Text('?');
       },

【讨论】:

    【解决方案9】:

    我使用了基本的 Image 小部件,并为错误添加了一个错误生成器,并为加载状态添加了一个 loadingBuilder,对于我使用 NetworkImageWithRetry 的实际图像,它会尝试多次加载图像并在最后给你一个错误,如果它无法加载图像。

    Image(
          image: NetworkImageWithRetry(
          'http://ImageUrl.png',),
          errorBuilder: (context, exception, stackTrack) => Icon(Icons.error,),
          loadingBuilder: (context, exception, stackTrack) => CircularProgressIndicator(),
        )
    

    【讨论】:

      【解决方案10】:

      对于使用 firebase 存储

      的用户

      上述方法对我一点帮助都没有。object is not found 在 firebase 中时,我得到了一个异常 "Null is not a type of List&lt;int&gt;"。 所以我决定验证 Unit8List 值是否有效。 如果它有效,那么我们就可以显示图像,否则使用任何小部件作为占位符。

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

      【讨论】:

      • 这里最好的解决方案!
      • @emmanuelagarry 很高兴它帮助了你
      【解决方案11】:

      我已经为同一个案例实现了这个 StatefulWidget,希望对你有帮助!!!

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

      【讨论】:

        【解决方案12】:

        https://pub.dev/packages/extended_image

        该插件可以轻松处理网络图像加载中的错误

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

        【讨论】:

          【解决方案13】:
          CachedNetworkImage(
            placeholder: (context, url) =>
                         Center(child: new CircularProgressIndicator()),
            errorWidget: (context, url, error) => 
                         Icon(Icons.error),
            imageUrl: image,
            fit: BoxFit.fill,
                        )
          

          【讨论】:

          • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。您可以在帮助中心找到更多关于如何写好答案的信息:stackoverflow.com/help/how-to-answer。祝你好运?
          猜你喜欢
          • 1970-01-01
          • 2011-11-20
          • 1970-01-01
          • 1970-01-01
          • 2018-03-07
          • 1970-01-01
          • 2012-07-27
          • 1970-01-01
          相关资源
          最近更新 更多