PR

[Flutter]Image.networkで画像の取得ができなかった時のエラー(例外)を抑制する方法

Flutter

はじめに

Flutterでネットワークから画像を表示して使う際に、Image.network()を利用している人が多いと思います。

しかし、画像が見つからなかった場合に例外(Exception)を吐きまくってしまうためログが膨大になってしまいます。

今回はそんな時にログを抑制する方法を紹介します。

環境

  • Flutter 2.2.2

ネットワークから画像が正しく取得できる場合

まずは正常系です。この場合は正しく画像が表示されます。

とくにログ等は表示されません。

            Image.network(
              'https://pbs.twimg.com/profile_images/1318213516935917568/mbU5hOLy_400x400.png',
              width: 128,
              height: 128,
            ),

ネットワークから画像が取得できない場合

次に異常系です。この際に、Exceptionのログが大量にでます。

            Image.network(
              // こんな画像のアドレスは存在しない.
              'https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png',
              width: 128,
              height: 128,
            ),

すると以下のようなエラーログがでます。

======== Exception caught by image resource service ================================================
The following _CastError was thrown resolving an image codec:
type 'Null' is not a subtype of type 'List<int>' in type cast

When the exception was thrown, this was the stack: 
#1      NetworkImage._loadAsync (package:flutter/src/painting/_network_image_io.dart:99:24)
<asynchronous suspension>
(elided one frame from dart:async)
Image provider: NetworkImage("https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png", scale: 1.0)
Image key: NetworkImage("https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png", scale: 1.0)
====================================================================================================

今回は1つだけですが、ListView等で複数表示する場合は、この例外がいくつも表示されるので嫌気が差します

対策方法

やり方は簡単でImage.networkerrorBuilderの設定をするだけです。

errorBuilderに設定するのは引数がBuildContext, Object, StackTrace戻り値がWidgetのメソッドです。

ImageErrorWidgetBuilder typedef - widgets library - Dart API
API docs for the ImageErrorWidgetBuilder typedef from the widgets library, for the Dart programming language.

戻り値のWidgetはエラーが発生した際(今回の場合は画像が見つからなかったとき)に変わりに表示するWidgetです。

            Image.network(
              'https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png',
              width: 128,
              height: 128,
              errorBuilder: (c, o, s) {
                return const Icon(
                  Icons.error,
                  color: Colors.red,
                );
              },
            ),

上記のサンプルコードの場合は画像が見つからなかったら、アイコンWidgetを表示するようにしてみました。ここではImage.asset等を利用しデフォルトのものを表示するみたいな処理に使えます。

またerrorBuilderを設定することで例外(Exception)を吐かなくなるので、エラーログはなくなります。

ただしエラーログがなくなってしまうと、どのURLがダメなのかがわかりにくいので、以下のようにログを表示しておくと後でデバッグしやすくなるのでおすすめです。

            Image.network(
              'https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png',
              width: 128,
              height: 128,
              errorBuilder: (c, o, s) {
                print('Load failed : ${c.toString()}');
                return const Icon(
                  Icons.error,
                  color: Colors.red,
                );
              },
            ),

すると以下のようなログ表示になります。

flutter: Load failed : Image(image: NetworkImage("https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png", scale: 1.0), frameBuilder: null, loadingBuilder: null, width: 128.0, height: 128.0, alignment: Alignment.center, this.excludeFromSemantics: false, filterQuality: low, dirty, dependencies: [_LocalizationsScope-[GlobalKey#83395], Directionality, MediaQuery, _EffectiveTickerMode], state: _ImageState#5079d(stream: ImageStream#019ae(MultiFrameImageStreamCompleter#ad239, unresolved, 1 listener), pixels: null, loadingProgress: null, frameNumber: null, wasSynchronouslyLoaded: false))

長いですがこの1行だけです。

サンプルコード

main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      backgroundColor: Colors.grey,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Image.network(
              'https://pbs.twimg.com/profile_images/1318213516935917568/xxxx.png',
              width: 128,
              height: 128,
              errorBuilder: (c, o, s) {
                print('Load failed : ${c.toString()}');
                return const Icon(
                  Icons.error,
                  color: Colors.red,
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

さいごに

最初にListViewで大量にこのエラーが出て泣きそうでした。

おすすめ参考書

コメント

タイトルとURLをコピーしました