[Flutter] イケてるICOOON MONOのSVGアイコンを使用する

Flutter Advent Calendar 2022」に参加させて頂きます!14日目です。

対象者

Flutterで開発をしていて、

  • ICOOON MONOのアイコンを使用したい人
  • ナイスなアイコンを使用したい人
  • SVGファイルを使用したい人

目的

Flutterでアプリ開発を使用していると、アイコンを使いたいケースがあります。マテリアルアイコンなどFlutterが標準で使用できるアイコンは色々ありますが、良いのが見つからない場合があります。そんなとき、ICOOON MONOというサイトが有益です。色々とアイコンが揃っており、商用の場合でもクレジットなしで使用できます。さらにSVGファイル(ベクター画像のため、テキストファイル)が用意されているので、小さいファイル容量で、大きく表示しても画像が粗くならず、色を変えるなどの小技も効きます。
そんなICOOON MONOのSVGファイルの使用方法を説明していきます。

悲報

残念ながら、ICOOON MONOのアイコンをプラグイン一つ設定するだけで使用できるようになる、などといういい話ではありません。サイトから検索して、ダウンロードして、SVGファイルとして表示します。

インストール

SVGを表示するので、SVGファイル表示用のパッケージ「flutter_svg」をインストールします。

flutter pub add flutter_svg

ファイルのダウンロード

ICOOON MONOのサイトへ行って、ファイルをダウンロードします。キーワード検索してナイスな画像を見つけたら、「256px」を選択して「SVG」をクリックしてください。ファイルがダウンロードされます。「color」はプログラムで設定するので、なんでもよいです。
画像のしましまの部分が透明で、黒い部分にプログラムで色をつけます。

ファイルをプロジェクトのアセットに入れる

以下の場合であれば、プロジェクト直下にassets/images/を作成して、ファイルを移動します。ファイル名が日本語なので、アルファベットにしておいた方が良いです、きっと。

flutter:
  uses-material-design: true
  assets:
    - assets/images/

設定後、以下もお忘れなく。

flutter pub get

また、ファイルを開いて(SVGファイルはテキストなので、エディタやIDEで大丈夫です)で、

<style>~</style>

があれば、削除しましょう(パッケージが対応していないため、実行時に警告が表示される。実行はできる)。

アセットの管理は、FlutterGenを使うと楽ですので、以下も参照ください。

[FlutterでAssets名を自動生成] 手っ取り早く始めるFlutterGen

コードの作成

SVGをStatelessWidgetとして表示する部分

SvgPicture.assetでSVGファイルを呼び出せます。せっかくなので、汎用性の高いクラスを作ってしまいます。
heightとwidthを指定しないとダウンロードで指定した大きさ(256px)になります。ちゃんと指定しましょう。ただアイコンの縦と横の長さが一緒なので、heightとwidthの片方だけ設定しておけば、もう一方も同じ値を設定してくれます。(値が大きい方に統一されるっぽいです)
色も指定できます。nullのときは、黒だと思われる。(ダウンロードの設定で変わるのかな)ライトモードとダークモード、ファイル一つで対応できますね。(今作ってるプロジェクトに白と黒の2種類のSVGファイルがあるから、早く消さないと!)

import 'package:flutter_svg/svg.dart';
import 'package:flutter/material.dart';

/// SVGのアイコンを表示します
class IcooonMonoIcon extends StatelessWidget {
  /// [path]のアセットのSVGファイルを[size]の大きさ、[color]の色で表示します。
  const IcooonMonoIcon({
    super.key,
    required this.path,
    required this.size,
    required this.color,
  });

  final String path;
  final double size;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return SvgPicture.asset(
      path,
      width: size,
      height: size,
      colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
    );
  }
}

colorは非推奨になり、 colorFilter: ColorFilter.mode(color, BlendMode.srcIn)という記述に変更になりました

呼び出し側のコード

_colorをColors.whiteとかにすれば、その色になります。サンプルでは、タップする毎に色が変わるように変数を割り当ててます。

IcooonMonoIcon(
            path: 'assets/images/bat.svg', size: 32, color: _color)

アプリ動作

ということで作成したアプリは次のような挙動になります。いつもは「+」のカウントアップアプリがバットのマークになってます。そして押す毎に色が変わります。ボタン部分をコピーして、ちょっと拡大してます。

全ソース

コピペでそのまま使える全ソースを貼り付けておきます。また
ここにもあります。

import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  Color _color = Colors.white;

  void _incrementCounter() {
    setState(() {
      _counter++;
      _color = _counter % 2 == 0 ? Colors.white : Colors.black;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: IcooonMonoIcon(
            path: 'assets/images/bat.svg', size: 32, 
            color: _color),
      ),
    );
  }
}

/// SVGのアイコンを表示します
class IcooonMonoIcon extends StatelessWidget {
  /// [path]のアセットのSVGファイルを[size]の大きさ、[color]の色で表示します。
  const IcooonMonoIcon({
    super.key,
    required this.path,
    required this.size,
    required this.color,
  });

  final String path;
  final double size;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return SvgPicture.asset(
      path,
      width: size,
      height: size,
      colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
    );
  }
}

まとめ

ということで、ICOOON MONOの紹介とSVGファイルをFlutterで表示する方法を紹介しました。マテリアルデザインでいいのが見つからなかったときは、ICOOON MONOでも探してみてください!ちなみにモノトーンのため、Flutterのみならず、ビジネス用途にも使えます!