👾
FlutterでSVG画像(flutter_svg)
SVG画像を扱いたい+α
- ローカルに格納したSVG画像ファイルを、Flutterの画面で表示したい
+α
- Preload(事前読み込み)な処理を対応したい
- 複数件まとめて、Preloadしたい
- 例外対応(画像pathが不正だった場合のケース)を隠蔽
- tyr{}catch的な処理をUI層で扱いたくない
※SVG画像: https://icooon-mono.com/15878-バイクアイコン/
対応概要
SVGLoaderクラスを作成して対応(内部でflutter_svgライブラリを利用)
事前準備
- svg画像を、プロジェクト内に格納し、pubspec.yamlにパスを指定。
https://docs.flutter.dev/ui/assets/assets-and-images#specifying-assets
// 例)"icon_bike.svg"を、assetsフォルダ直下に配置した場合のパス指定
assets:
- assets/icon_bike.svg
- flutter_svg を、pub getして導入
対応後の利用イメージ
// 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