2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Material Symbolsを簡単にFlutterプロジェクトに組み込めるツールを作ってみた

Last updated at Posted at 2023-01-15

みなさんはFlutterアプリでアイコンが必要な時、どうしていますか?Material DesignならIconsクラス、iOSならCupertinoIconsクラスでしょうか?Flutterは便利なコンポーネントが豊富に揃っているのが良いですよね。ですが足りないものが1つあります。そう、それはMaterial Symbolsです。残念ながらFlutterはMaterial Symbolsを標準でサポートしていません。そこでfms(flutter-material-symbols) というCLIツールを作りました。fmsを使えばIconsCupertinoIconsと同じ感覚でMaterial Symbolsを使えるようになります。アイコンのリソースを手動でダウンロード・管理する必要もありません。

以下前置きです。本題はココから。

背景

Material Symbols:2022年に登場したMaterial Designの新しいアイコンセット

Material Designの最新版であるMaterial3ではアイコンも刷新され、これまでのMaterial Icons(以下、MIcons)に代わりMaterial Symbols(以下、MSymbols)が新たに導入されました。MIconsとMSymbolsの大きな違いは可変フォントになったことです。そのためMSymbolsのアイコン(以下、シンボル)はスタイルと4つの軸、合わせて5つのパラメータによってカスタマイズすることができます。それぞれのパラメータがどう作用するのかは公式のギャラリーサイトを見るのが早いでしょう。

material-symbols-axes

Flutter SDKはMaterial Symbolsに未対応

現時点ではFlutter SDKの中にMSymbolsは含まれていません。Iconsクラスは?と思う方もいるかもしれませんが、残念ながらIconsクラスはMIconsしかサポートしていません。

Identifiers for the supported Material Icons.

Icons class - material library - Dart API

公式対応はもう少し先になりそう

GitHubではIssueが上がっており、現在対応が進められている状況です。ただ昨年の9月頃からあまり動きが見られず、Stableチャンネルに実装されるのはまだ先になりそうです。

ちなみにですが、Design DocによるとMSymbolsはSDKには含まれず、別パッケージとして提供される予定だそうです。またシンボルのカスタマイズは、IconWidgetのコンストラクタに4つの軸に対応した引数をそれぞれ追加することで実現されるようです。実装はまだのはずですが、Iconクラスのドキュメントにはすでに新しいコンストラクタ引数が追加されています。

Icon(IconData? icon, {Key? key, double? size, double? fill, double? weight, double? grade, double? opticalSize, Color? color, List<[Shadow](https://api.flutter.dev/flutter/dart-ui/Shadow-class.html)>? shadows, String? semanticLabel, TextDirection? textDirection})

Creates an icon.

Icon constructor - Icon - widgets library - Dart API

自前でなんとかする方法はある

じゃあMSymbolsはまだ使えない?いいえ、そんなことはありません。幸いなことにMSymbolsはOSSであり、全てのリソースがGitHubで公開されています。またシンボルを一覧できる公式のギャラリーサイトからは簡単にSVGファイルが手に入りますので、あとはflutter_svgなどのパッケージを使って

child: IconButton(
  icon: SvgPicture.asset('path/to/svg/file'),
),

とかすればすぐにプロジェクトにMSymbolsを組み込めます。

とはいえこれだとパスを書かないといけないし、IDEの補完も効きません。できればIconsクラスみたいにIcons.homeとか書きたいですよね。そのためには拾ってきたSVGを1つのアイコンフォントにまとめる必要があります。それにはnode.jsパッケージのfantasticon が使えます。これはSVGからアイコンフォントを生成してくれるCLIツールです。アイコンフォントができたら、IconsCupertinoIconsのようなラッパークラスを書きましょう。

ラッパークラスなんてどうやって書くんじゃい!安心してください、icon_font_generatorがあります。これはSVGからアイコンフォントの生成、そしてラッパークラスの生成まで全部やってくれるCLIツールです。しかもDart製なのでpub getでインストールできます。さあ、あとは必要なSVGを拾い集めてくるだけです。

で、fmsが生まれたってわけ

「あとは必要なSVGを拾い集めてくるだけ」の部分も自動化したのがfms(flutter-material-symbols) パッケージです。fmsは内部でfantasticonとicon_font_generatorを利用しているため、リスペクトの意味を込めて上記のパッケージを紹介いたしました。fmsを使えば設定ファイルからSVGの収集、アイコンフォント、ラッパークラスの生成までコマンド一発です。リソースの管理を手動でする必要はありません。

なぜわざわざ「生成」するのか?

MIconsは全アイコンのIconDataIconsクラスにstataic変数として定義されています。一方でMSymbolsの場合シンボル自体が2500種類ほどあり、それぞれが5つのパラメータを持つためその組み合わせは途方も無い数になります。そのため各シンボルの全バリエーションをstatic変数として提供する場合、巨大なアイコンフォントをパッケージに含めることになります。そのためfmsでは必要なアイコンだけを含むフォントを生成するという方針をとりました。

fmsの使い方

全て説明すると長くなってしまうので、ここには概要だけ記します。詳しい使い方に興味のある方はREADME.mdをご覧ください。

インストール

pubコマンドでPub.devからインストールできます。

$ flutter pub add --dev fms

fmsは内部でnode.jsパッケージのfantasticonを利用しています。バージョン11以降のnode.jsが入っていない場合は別途インストールが必要です。

$ node --version   
v18.12.1

Getting started

例としてNavigationBarのタブに使うアイコンを用意するケースを考えます。選択状態と未選択状態を区別するために2種類のホームアイコン🏠(アウトラインのみ、塗りつぶし)を作ります。

kyki80zd-nav-bar_navigation_3P_AdobeExpress-2.gif

  1. 設定ファイルを書く

    fmsの設定ファイルはYAMLで書きます。この例ではmy_symbols.yamlという名前でプロジェクトルートに置いていますが、どこに置いても構いません。設定ファイルには生成されるアイコンフォントのファミリー名、アイコンフォントとラッパークラスの出力先、そして使用したいシンボルの情報を記述します。

    # project_root/my_symbols.yaml
    
    family: MySymbols # ファミリー名
    
    output:
      flutter: lib/src/my_symbols.dart # ラッパークラス
      font: assets/my_symbols.ttf # アイコンフォント
    
    # 使用したいシンボルの情報
    symbols:
      home: Home # 未選択状態のHomeシンボル(アウトラインのみ)
      home_selected: # 選択状態のHomeシンボル
        name: Home
        fill: true # 塗りつぶし
    

    fmsはこの設定ファイルからラッパークラスMySymbolsを生成し、各アイコンにはMySymbols.home_selectedのようにアクセスできるようになります。

  2. アイコンフォントとラッパークラスを生成

    以下のコマンドはassets/my_symbols.ttfにアイコンフォントが、lib/src/my_symbols.dartにラッパークラスをそれぞれ生成します。

    $ flutter pub run fms build my_symbols.yaml
    

    実行するとlib/src/my_symbols.dartに下記のようなラッパークラスが生成されます。

     import 'package:flutter/widgets.dart';
    
     // GENERATED CODE - DO NOT MODIFY BY HAND
     // ignore_for_file: non_constant_identifier_names
     // ignore_for_file: constant_identifier_names
     @immutable
     class _MySymbolsData extends IconData {
       const _MySymbolsData(int codePoint, this.name)
           : super(
               codePoint,
               fontFamily: 'MySymbols',
             );
    
       final String name;
     }
    
     @immutable
     class MySymbols {
       const MySymbols._();
    
       static const home_focused = _MySymbolsData(0xf102, 'home_focused');
       static const home = _MySymbolsData(0xf103, 'home');
    
       static const all = <String, _MySymbolsData>{
         'home_focused': home_focused,
         'home': home,
       };
     }
    
  3. アイコンフォントの情報をpubspec.yamlに追加

    MySymbolsを使うためには対応するアイコンフォントをFlutterに認識させる必要があります。pubspec.yamlflutter:セクションに生成されたmy_symbols.ttfを追加します。

    またフォントファイルをlib/以外の場所(例えばassets/)に置く場合はassets:セクションでアセットとして追加することも忘れないでください。

    ...
    
    flutter:
      assets:
        - assets/
      fonts:
        - family: MySymbols # ファミリー名
          fonts:
            - asset: assets/my_symbols.ttf # フォントファイル
    
  4. 生成されたアイコンを使う

    あとはIconsCurpetinoIconsと同じようにMySymbolsクラスから必要なアイコンを利用するだけです。もちろんconstも使えますし補完も効きます。やったね!

    import 'package:your_package/src/my_symbols.dart';
    import 'package:flutter/material.dart';
    
    Widget homeNaviDest() {
      return NavigationDestination({
        icon: const Icon(MySymbols.home),
        selectedIcon: const Icon(MySymbols.home_selected),
      });
    }
    

終わりに

Material Symbolsでググってもあまり情報が出てこないのだけれど、もしかして認知度低いのか、、?

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?