👾

FlutterでSVG画像(flutter_svg)

2024/01/24に公開

SVG画像を扱いたい+α

  • ローカルに格納したSVG画像ファイルを、Flutterの画面で表示したい

  • Preload(事前読み込み)な処理を対応したい
  • 複数件まとめて、Preloadしたい
  • 例外対応(画像pathが不正だった場合のケース)を隠蔽
    • tyr{}catch的な処理をUI層で扱いたくない

※SVG画像: https://icooon-mono.com/15878-バイクアイコン/

対応概要

SVGLoaderクラスを作成して対応(内部でflutter_svgライブラリを利用)

事前準備

// 例)"icon_bike.svg"を、assetsフォルダ直下に配置した場合のパス指定
  assets:
    - assets/icon_bike.svg
  • flutter_svg を、pub getして導入

https://pub.dev/packages/flutter_svg/install

対応後の利用イメージ

// Futureビルダーで、SVG画像を読み込み表示するコード
FutureBuilder(
  future: SVGLoader.loadOrNull(path: "assets/icon_bike.svg"),
  builder: (BuildContext context, AsyncSnapshot<SvgPicture?> picture) {
    if (picture.hasData) { // SVG画像を表示
      return Container(margin: const EdgeInsets.all(20), child: picture.data,);
    } else { // 画像がなかった場合の表示
      return Container(child: Center(child: Text("NOImage..."),),); 
    }
  },
);

実装

  • SVGLoaderクラスを実装
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/svg.dart';

/// SVG画像の読み込みクラス
class SVGLoader {

  // 画像を読み込み
  static Future<SvgPicture?> loadOrNull({
    required String path,
  }) async {
    print("image path: $path");

    try {
      // 指定したpath画像の存在チェック(ない場合、catchに入ります)
      await rootBundle.load(path);

      SvgPicture pic = SvgPicture.asset(path);
      return pic;

    } catch (e) {
      print('指定されたパスのAseets画像がないエラーです. \n ${e.toString()}');
      return null;
    }
  }

  // 画像を事前読み込み(プリロード)
  // refs: https://github.com/dnfield/flutter_svg/issues/841
  static Future preload(List<String> pathList) async {

    // 画像のプリロード処理
    print("Start! - image preload");
    pathList.forEach((path) async {
      print("load...");

      // MEMO: flutter_svg  2.0.1〜の プリロード処理
      var loader = SvgAssetLoader(path);
      await svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));

      // 旧処理
      // await Future.wait([
      //   precachePicture(ExactAssetPicture(
      //     SvgPicture.svgStringDecoderBuilder,
      //     // SvgPicture.svgStringDecoder,
      //     path,
      //   ),
      //     null,
      //   ),
      // ]);

    });

    print("Done! - image preload");
  }
}

SVGの表示コード

  • Widget内で、FutureBuilderを使い、nullチェックの分岐対応
class TESTPAGE extends StatelessWidget {
  const TESTPAGE({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(body: Center(
      // Futureビルダーで、SVG画像を読み込み表示する
      child: FutureBuilder(
        future: SVGLoader.loadOrNull(path: "assets/icon_bike.svg"),
        builder: (BuildContext context,
            AsyncSnapshot<SvgPicture?> picture) {
          if (picture.hasData) {
            return Container(
              width: 100,
              height: 100,
              margin: const EdgeInsets.all(20),
              child: picture.data,
            );
          } else { // 画像がなかった場合の表示
            return Container(child: Center(child: Text("NoImage..."),),);
          }
        },
      ),
    ),
    );
  }

}

SVGをまとめてプリフェッチするコード

final svgPathList = ["assets/hoge1.svg", "assets/hoge2.svg", "assets/hoge3.svg", "assets/hoge4.svg", "assets/hoge5.svg", "assets/hoge.svg6", ];

// 全画像Pathをプリロード実施
await SVGLoader.preload(svgPathList);
  • 用途としては、SVG画像を一覧表示する画面の遷移前に、プリフェッチを呼ぶことで、画像表示が早くなる。(量が多い・表示に時間がかかっていた場合に有効)

Discussion