FigmaのMaster fileを限りなくFlutterの実装に寄せて作った話

# ****限りなくFlutterに寄せて作ったFigmaのマスタファイルはメンテしたくなるはず****

### 要旨

- Figmaのマスタファイルがメンテナンスされないのは、運用や体制が悪いのではなく、マスタファイルに価値を感じられていないからという仮説
- Figmaは、**_view.dartのpseudocodeとして作ることが可能
- model, view modelに基づく制御のアウトカムをFigmaコンポーネントのインスタンスで表現できる
- マスタファイルをドキュメントとして開発チーム以外にも展開
- マスタファイルをメンテナンスする動機となるような価値を付与

本記事の執筆者とマスタファイルの制作者は一介のデザイナです。チョットデキるとは言えない程度にフロントエンドも書きます。

## 課題

Figmaのマスタファイルがメンテナンスされないのは、由々しき課題です。

> そもそもマスタファイルなんて存在しません。
> 

なんてこともよくあると思います。

デザイナは、ユーザに届けている最新の版を保てていないFigmaが目の前に作業を始めると、右往左往してしまいます。そして、つぎはぎだらけのフランケンシュタインみたいなデザインになっていくことは必至です。

また、デザイナとエンジニアの職責や関係性にもよりますが、Figmaがただの下書きになってしまっていることもあるあるだと思います。

> デザイナは、Figmaの通りに作ってくれないじゃん…、とマウスを投げ捨てる

> エンジニアは、めちゃ面倒なUI作ってんじゃん…、とキーボードを投げ捨てる

そして、
> そもそもマスタファイルなんて、規模のあるチームじゃないと整備しても意味ないじゃん

と、マスタファイルがないことを正当化してしまいたい気持ちもわかります。Figmaのマスタファイルを作れない、メンテナンスできない理由は山ほどあります。

私は、総じて、Figmaやマスタファイルに価値を感じられていないことが課題と捉えました。

Figmaのメンテナンスがされないという課題に対して、”メンテナンスを徹底する”というわけにもいかないです。人は怠惰な生き物ですので、必ずメンテナンスされなくなります。

皆がハッピーになるマスタファイルを作れたら、メンテナンスもできるのではないでしょうか。

## 前提と背景

- エンジニアもデザイナも工数が足りていないスタートアップの話
- Flutterで作るiOSとAndroidアプリの話
- 大きめのデザイン改修があったタイミングで作業した
- 以前はFigmaのマスタファイルが存在してなかった
- 2023年7月頃に作業していた（FigmaのVariablesやDev Modeのアナウンス前）

そもそもマスタファイルとは…
> A master file in Figma is a centralized document that contains a comprehensive collection of design elements and assets which are used across multiple projects within an organization. It is a key component of a design system in Figma, ensuring consistency and efficiency in the design process. – *ChatGPT 4*.
>> Figmaのマスターファイルは、組織内の複数のプロジェクトで使用されるデザイン要素とアセットの包括的なコレクションを含む中央集約されたドキュメントです。これはFigmaのデザインシステムの重要な要素であり、デザインプロセスの一貫性と効率を保証します。– *ChatGPT 4*

## 方法

メンテナンスの徹底は棚上げして、まずはFigmaのマスタファイルの価値を設定しました。

1. デザイナとエンジニアのための設計資料
2. 皆が参照できる仕様確認のドキュメント

Figmaをデザイナのモノにしない、開発チームならびに会社の資源だという位置付けを作りたかったゆえです。

そして、マスタファイル作成の作業方針として、Flutterの実装と限りなく近づけることにしました。エンジニアに設計資料としての価値を感じてもらうためです。Figmaは見た目を描いているだけのものという認識ではメンテナンスする気にならないのはもちろん、ましてやFigmaのレイヤ構成やコンポーネントのプロパティを確認することもないでしょう。

ここでは、制作したマスタファイルのGood pointsを4つ紹介します。

1. ファイル構成をコードベースと揃える
2. Viewを表現するコンポーネントの名前をコードベースと揃える
3. レイヤやコンポーネント設計をPseudocodeっぽくFlutter実装に寄せている
4. ドキュメントとして開発チーム外にも共有

### 1. ファイル構成をコードベースと揃える

FigmaのPage単位がFolderに当たります。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/672a96f3-5d09-dae7-6d8e-543a2bd66802.png)


### 2. Viewを表現するコンポーネントの名前をコードベースと揃える

MVVMでいうViewにあたるコンポーネントを一対になるように作成しています。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/e40beb28-91ef-48fc-b284-5a6fdbfb14e7.png)


### 3. レイヤやコンポーネント設計をPseudocodeっぽくFlutter実装に寄せている

具体的には、以下のようなことをしています。

- コンポーネント名をFlutterのWidget名と合わせる
- Auto layoutのフレームの名前には、SingleChildScrollView, ListView, Row, Columnなどを当てる
- Flutter Iconクラスと合わせたアイコン名にする
- 実装に必要なロジックが絡む部分をFigmaの機能が許す限り実装と合わせる

最後に挙げたFigmaでのロジック表現について、イメージを添えて解説します。

ユーザーの権限によって、ある機能を表示/非表示したいことがあります。コードでは、Viewの中に、ModelやらViewModelに基づく条件節で分岐する記述をするはずです。

イメージ内の例では住所変更の動線をユーザーによっては非表示にするため、canChangeDeviceAddressというBoolean propertyをdevice_setting_viewというコンポーネントに作って表現しています。

![3_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/7ef6351b-aee5-3d27-ee75-7aa3ec1cabc0.png)


参考までに、Viewの記述はだいたいこんな感じ、というのを置いておきます。

```dart:lock_device_setting_view.dart
...
    ListView(
      children: [
        ListTile(
          title: const Text('名前・アイコン'),
          trailing: const Icon(Icons.arrow_forward_ios_rounded),
          onTap: viewModel.toNameSetting,
        ),
        ListTile(
          title: const Text('住所'),
          subtitle: Text(model.location.address),
          trailing: model.canChangeDeviceAddress
              ? const Icon(Icons.arrow_forward_ios_rounded)
              : null,
          onTap: model.canChangeDeviceAddress
              ? () => router.push(AddressSettingRoute(device: model.device))
              : null,
        ),
        
        ...
      ]
    )
...
```

Figmaでのロジック表現についてもう一つ、Modalを例にして説明します。

ユーザーのアクションに合わせてModalを表示するとき、デザイナ的には、別の画面（コンポーネント/フレーム）を用意して表現しがちだと思います。しかし、コードではModalも一つのViewの中に記述されます。

デバイスのファームウェアアップデートを例に、イメージ付きでウォークスルーします。

1. ファームウェアアップデートの動線が表示
2. ユーザーがList item（ListTile）をタップ
3. modalが開く
4. ユーザーが実行するボタンをタップ
5. loadingが走る
6. snackbarでフィードバックが出る

ここまでの一連の流れを、Figmaでも一つのコンポーネントで表現しています。

![3_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/8ddf4056-3ed1-1879-3924-881109e88675.png)
![3_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/b9816eb6-72a5-d75f-c2eb-244fbab2c59a.png)


例の如く、Viewの記述はだいたいこんな感じ、というのを置いておきます。

```dart:lock_device_setting_view.dart
	...
    
    runFirmwareUpdate() async {
				showDialog(...);
        await withLoading(
          title: const Text("ファームウェアをアップデート中…"),
          func: () async {
            await viewModel.runFirmwareUpdate();
            ScaffoldMessenger.of(context).showSnackBar(...);
          }
        );
      }
    
    return ListView(
      children: [

        ...
        
        ListTile(
          title: const Text('ファームウェア'),
          subtitle: Text(device.firmwareVersion),
          trailing: model.isFirmwareUpdateAvailable
              ? const Icon(Icons.refresh_rounded)
              : null,
          onTap: model.isFirmwareUpdateAvailable
              ? runFirmwareUpdate()
              : null,
        ),
        if (model.isFirmwareUpdateAvailable)
            Row(
                children: [
                  const Icon(Icons.info_rounded),
                  const SizedBox(width: 8.0),
                  const Text('アップデート可能'),
                ],
            )

      ...
```

### 4. ドキュメントとして開発チーム外にも共有

ユーザーが見る画面を押し並べたページを用意しました（UI / ViewのFigmaファイルに）。これで、アプリを横断的に全体を把握できるようになりました。仕様の確認にも使えるドキュメントの完成です。

加えて、下記のイメージにあるように、helpページを一括で見たいときには、Figma上でコンポーネント名を検索することで、簡単に探すことができます。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/75bd9782-b6b0-0734-ba57-03b12cce719c.png)

<br>
<details><summary>Figma重くなりそう…という方向けに、余談</summary>

レイヤ数は197,000+になりました。M2 Proかつ32GB MemoryのMacbook Proという環境では、全く作業に不自由ないです。ただし、Branch運用を怠ってConflictが嵩むと、メモリを食いすぎてMergeできなくなるのでご注意ください。
![memory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627450/c482702b-f939-9851-f703-8725b697676b.png)


</details>



## 結果

社内で展開したところ、とても好評でした。

エンジニアには、Flutterに寄せたFigmaのおかげで実装が楽だったと喜んでいただけました。

> デザインの段階でほぼ View の実装が終わったといっても過言ではない。あと ViewModel と API の繋ぎ込みで実装終わっちゃう
> 

> デザインの段階で細かい条件分岐含むアウトプットの解像度が非常に高くなるので手戻りが劇的に減った
> 

> マジですごい。こんなちゃんとやってるとこある？っていうくらいちゃんとしてる
> 

Quality Assuranceのチームにも、Figmaをよく見ていただけています。ことあるごとに、Figmaを気にしてくれる方々が増えました。

今後もメンテナンスしていけるかは、乞うご期待です。

## 小噺

- マスタファイルの構成は３つに分けてます
    1. Assets
    2. Components / Widgets
    3. UI / View
- 大きなデザイン改修部分はFigma先行でそこそこきっちり作れました。もちろん、実装中に出た変更やフィードバックは出ます。それらは並行してマスタファイルに反映したりしてます。
- それ以外の部分はコードリーディングして、Figmaへ人力で描き起こしました
- 悲しいポイント
    - レイヤの昇順・降順がDev Modeでは入れ替わる（お絵描きにおけるレイヤの概念と、コーディングにおけるレイヤの概念が違うからどうしようもない）
    - コンポーネントのBooleanでやったけど、今後はVariablesでやるのがベストプラクティスになると思われる
- Flutter Iconクラスでのアイコン名は、Material Symbolsのアイコン名と名前が違うことがあります。例えば、Material Symbolsで[Schedule](https://fonts.google.com/icons?icon.query=schedule)と呼ばれるアイコンは、Flutter Iconクラスでは[access_time](https://api.flutter.dev/flutter/material/Icons/access_time-constant.html)です。FigmaのMaterial Symbols Pluginで持ってきたアイコンをちまちまリネームするという作業が発生しました。


## 最後に

本記事の内容は、2023年のDesignshipとFlutterKaigiで展示してました。ブースに来て話を聞いてくださった方々、ありがとうございました。おかげさまで執筆の重い腰を上げることできました。

ビットキーは積極的にテック系イベントに参加して、プロダクトの紹介やその他イベントに合わせたコンテンツを用意しています。Figmaも公開できる時はしています。ご興味ある方は、ぜひお立ち寄りください。

4日目の [株式会社ビットキー Advent Calendar 2023](https://qiita.com/advent-calendar/2023/bitkey) は @utisam が担当します！

---

この記事は、keyball61で書きました。