FlutterでSVGの色を動的に変更する実装方法
はじめに
SVGファイルは拡大縮小しても品質が劣化しない優れたベクター画像形式です。アプリ内で色を動的に変更したい場合、単純な画像置き換えでは対応できません。
今回は、SVGファイル内の特定の要素の色を自由に変更できるウィジェットの実装方法をご紹介します。
SVG の色を動的に変更するとできること
百聞は一見に如かず。という事で下記をご覧ください。
私が現在開発中のアプリではテーマカラー選択ができるのですが、
ワンちゃんの画像のスカーフの部分の色だけが
テーマカラーに合わせて変わっていると思います。
こんなふうに、動的に色を変えられるようになるのがメリットです。
SVG ファイルの準備
まず、動的に色を変更させるsvgファイルを用意します。
svg形式で取得可能なフリー素材を用いればOKですが、迷ったらこのサイトから探してみてください。
unDraw
色を変更したい要素に id=
で適当な文字列を渡します。
例では id="change_color_target"
を追加しています。
<svg xmlns="http://www.w3.org/2000/svg" ...>
<!-- 色を変更したい要素にIDを追加 -->
<path id="change_color_target"
d="..."
fill="#000000"/>
<!-- 他の要素 -->
<path d="..." fill="#3f3d56"/>
</svg>
上記例では視認性向上のために改行しているが、
実際のSVGファイルでは以下のように1行で記述すること:
<svg xmlns="http://www.w3.org/2000/svg" ...><path id="change_color_target" d="..." fill="#000000"/><path d="..." fill="#3f3d56"/></svg>
改行を避けるべき理由
XMLパーサーの挙動
FlutterのSVGパーサーは、XMLの構文解析を厳密に行います
改行やインデントが入ることで、予期せぬ場所で要素が分割される可能性があります
これにより XmlParserException: ">" expected at XX:XX
のようなエラーが発生することがあります
pubspec.yaml の準備
まず、必要なパッケージをpubspec.yaml
に追加します:
dependencies:
flutter_svg: ^2.0.14
次に、SVGファイルをアセットとして登録します:
flutter:
assets:
- assets/images/example.svg
SVGファイルはassets/images
ディレクトリに配置することを推奨します。異なるディレクトリに配置する場合は、パスを適切に変更してください。
パッケージのインストールとアセットの登録が完了したら、以下のコマンドを実行します:
flutter pub get
実装
ColorChangeSvg
クラスを作成します:
このファイルは特に変更不要
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
/// SVGファイル内の特定の要素の色を変更するウィジェット
class ColorChangeSvg extends StatelessWidget {
final String assetName;
final Map<String, Color> colorMapping;
final double width;
final double height;
const ColorChangeSvg({
required this.assetName,
required this.colorMapping,
required this.width,
required this.height,
super.key,
});
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: DefaultAssetBundle.of(context).loadString(assetName),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SizedBox(width: width, height: height);
}
String svgContent = snapshot.data!;
// 各ターゲットに対して色を変更
colorMapping.forEach((targetId, color) {
final hexColor = '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}';
final pattern = RegExp('(id="$targetId"[^>]*?)(fill="[^"]*")', multiLine: true);
svgContent = svgContent.replaceAllMapped(pattern, (match) {
final prefix = match.group(1) ?? '';
return '$prefix fill="$hexColor"';
});
});
return SvgPicture.string(
svgContent,
width: width,
height: height,
fit: BoxFit.contain,
);
},
);
}
}
使用方法
このウィジェットは以下のように使用できます:
// 単一の色を変更する場合
ColorChangeSvg(
assetName: 'assets/images/example.svg',
colorMapping: {'change_color_target': Colors.blue},
width: 150,
height: 150,
)
// 複数の色を変更する場合
ColorChangeSvg(
assetName: 'assets/images/example.svg',
colorMapping: {
'target_1': Colors.yellow,
'target_2': Colors.red,
'target_3': Colors.green,
},
width: 150,
height: 150,
)
デモページの実装例
下記のsvgファイルを使用します。
練習用にそのまま使用してOK
<svg xmlns="http://www.w3.org/2000/svg" width="888" height="483.61099" viewBox="0 0 888 483.61099" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="m1,421.53481h887v-2H1c-.55228,0-1,.44772-1,1h0c0,.55228.44771,1,1,1Z" fill="#3f3d56"/><path d="m406,438.53481c5,24,33.5-5.5,33.5-5.5l34-4s21.5.5,35.5,11.5,23.5-3.5,23.5-3.5c15.77283,5.2576,35.09692,5.23627,51.83264,3.55252,24.11597-2.42627,43.44604-21.04041,46.76044-45.05038,18.69678-135.44147-84.09314-202.00211-84.09314-202.00211l-30-81s9.5-23.5,12.5-44.5-19-41-46-61c-11.8125-8.75-25.34766-8.12109-36.83716-4.64502-12.88892,3.89946-24.13214,11.9183-32.4397,22.51616-6.88251,8.77995-18.68735,21.21468-31.66315,23.36885-4.48999.73999-8.91998,2.52002-13.06,5.76001-4.28003,3.34998-6.16998,8.94-6.22998,15.89001-.32001,30.35999,24.75,66.90998,46.72998,72.60999,27,7,12.5,73.5,12.5,73.5-44,62-13,131-13,131l-8,56s-.5,11.5,4.5,35.5l.00006-.00003Z" fill="#3f3d56"/><path d="m553,309.53481l-11,137s15,38-36,36-21-80-21-80l16-102" fill="#3f3d56"/><path d="m509.79492,483.61099c-1.24707,0-2.52344-.02539-3.83398-.07715-11.29004-.44238-19.60352-4.60059-24.71094-12.3584-14.60254-22.18164,1.63867-65.90625,2.77832-68.89941l15.9834-101.89648c.08594-.5459.59375-.91309,1.14355-.83301.54492.08594.91797.59766.83301,1.14355l-16,102c-.01172.06934-.0293.1377-.05469.2041-.1748.45215-17.25195,45.55566-3.01172,67.18359,4.73242,7.18652,12.50977,11.04199,23.11719,11.45801,16.65234.64941,27.85254-2.90332,33.29785-10.56836,7.12402-10.02832,1.78711-23.92676,1.73242-24.06641-.05566-.1416-.07812-.29492-.06641-.44629l11-137c.04395-.5498.5166-.95117,1.07715-.91699.55078.04492.96094.52637.91699,1.07715l-10.98242,136.77539c.72656,1.96094,5.28223,15.4043-2.03906,25.72363-5.41699,7.63574-15.89844,11.49707-31.18066,11.49707Z" fill="#2f2e41"/><path d="m462,337.53481s3,127-60,124-6-43-6-43l5.5-15.5" fill="#3f3d56"/><path d="m404.16211,462.58657c-.73145,0-1.4668-.01758-2.20996-.05273-17.98828-.85645-28.15186-4.88672-30.20801-11.97852-3.7041-12.7793,20.43945-30.52148,23.41895-32.65332l5.39453-15.20117c.18457-.52148.75586-.79297,1.27637-.6084s.79297.75586.6084,1.27637l-5.5,15.5c-.06934.19629-.19824.36523-.36816.48438-.26367.18555-26.37744,18.68457-22.90869,30.64551,1.79248,6.18066,11.34131,9.72559,28.38232,10.53711,11.34863.5459,21.29688-3.27051,29.55078-11.3291,30.95215-30.21582,29.4209-110.83789,29.40137-111.64844-.0127-.55176.42383-1.00977.97656-1.02344.52246.00098,1.00977.4248,1.02344.97656.0791,3.3457,1.57227,82.29785-30.00293,113.125-8.12793,7.93555-17.82031,11.9502-28.83496,11.9502Z" fill="#2f2e41"/><path d="m363.27002,69.92484c7.17999.85999,18.41998.81995,27.72998-5.39001,9.96997-6.65002-.38-12.85004-8.44-16.26001-4.48999.73999-8.91998,2.52002-13.06,5.76001-4.28003,3.34998-6.16998,8.94-6.22998,15.89001Z" fill="#2f2e41"/><path id="target_2" d="m479.2984,51.94388s-17.44968-44.81639,7.54245-45.4435,44.73257,38.20994,44.73257,38.20994c0,0,62.72797,114.34248-23.21378,108.17637-85.94171-6.16615-44.87375-51.81067-44.87375-51.81067,0,0,19.60165-24.09928,15.81253-49.13211h-.00003v-.00002h0s0-.00002,0-.00002Z" fill="#2f2e41"/><path id="target_3" d="m575.03857,386.85064c11.06787,4.92365,22.63171,9.948,34.73578,9.46738s24.84155-8.27557,26.72772-20.24146c.97363-6.17697-.95746-12.80396,1.40759-18.59268,3.18213-7.78867,13.44672-10.79233,21.42578-8.12323s13.84076,9.55188,18.04358,16.84058c7.86328,13.63672,11.0141,31.53461,2.58307,44.82779-7.30878,11.52368-21.17084,16.73105-34.06519,21.19748-17.17535,5.94928-36.35114,11.854-52.99396,4.54617-16.7381-7.34964-25.85565-28.58435-19.65753-45.7821" fill="#3f3d56"/><path id="target_1" d="m538.1142,170.92143s22,11,11,23-79,46-104,41-31-20-31-24,1.88583-52.38661,9.88583-50.38661,58.11417,47.38661,114.11417,10.38661Z" fill="#00bfa6"/></svg>
実際の使用例はこちら:
import 'package:flutter/material.dart';
import 'package:knocqnow/presentation/components/common/color_change_svg.dart';
/// SVGの色変更機能のデモページ
class SvgMockPage extends StatelessWidget {
const SvgMockPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('SVG Color Change Demo'),
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Single Color Change'),
SizedBox(height: 16),
ColorChangeSvg(
assetName: 'assets/images/example.svg',
colorMapping: {'target_1': Colors.blue},
width: 200,
height: 200,
),
SizedBox(height: 32),
Text('Multi Color Change'),
SizedBox(height: 16),
ColorChangeSvg(
assetName: 'assets/images/example.svg',
colorMapping: {
'target_1': Colors.yellow,
'target_2': Colors.red,
'target_3': Colors.green,
},
width: 200,
height: 200,
),
],
),
),
);
}
}
SVGファイルで色を変更したい要素には、必ず対応するid
属性を指定する必要があります。
例えば、colorMapping
でtarget_1
を指定する場合、SVGファイル内にid="target_1"
の要素が存在している必要があります。
完成イメージはこちら
あとは colorMapping: {'target_1': Colors.blue},
の色の部分を
現在使用中のアプリのテーマカラー用の変数に変更してあげれば、
テーマカラーに合わせてSVGファイルの色を動的に変更可能!
トラブルシューティング
色変更が正しく反映されない場合は、以下の点を確認してください:
- SVGファイルが
pubspec.yaml
のassets
に正しく登録されているか - SVGファイル内の要素に適切な
id
属性が設定されているか -
colorMapping
で指定したIDがSVGファイル内のIDと完全に一致しているか
問題が解決しない場合は、以下のコマンドを実行してキャッシュをクリアしてください。
一度でうまくいかない場合は何回か試してみてください:
flutter clean
flutter pub get
まとめ
このウィジェットを使用することで、SVGファイルの色を動的に変更できるようになります。
是非お試しください。