メルカリ ハロのデザインシステムとFlutter

こんにちは。メルカリ ハロのモバイルチームのEMの@atsumoです。
この記事は、連載:メルカリ ハロ 開発の裏側 – Flutterと支える技術 –の3回目と、
Mercari Advent Calendar 2024 の7日目の記事です。

はじめに

メルカリ ハロは2024年3月にリリースされた、Flutterを用いて開発されたアプリケーションです。本記事では、デザインシステムの導入によって実現した開発効率の向上と、その具体的な運用方法について共有いたします。

目次

  1. デザインシステムの概要
  2. メルカリ ハロのデザインシステム Componentのご紹介
  3. FigmaからFlutterへの実装について
  4. Componentを使った画面実装
  5. 1年経過して見えてきた課題
  6. 今後の展望
  7. まとめ

それでは、順を追って説明していきます。

デザインシステムの概要

デザインシステムについて触れたいと思います。

私たちはデザインシステムを『サービスにおけるUXやUIの一貫性を保つための要素と仕組み』と捉えています。デザインシステムを活用することで、個別のコンポーネントや色やタイポグラフィの設定だけではなく、サービス全体におけるそれらの一貫性を担保しながら、開発体験および生産性を向上させることができます。

デザインシステムによってデザイナーの創造性を制限してしまうのではないか?など自由度が下がってしまうのではないかと懸念されることがあるかも知れませんが、私がデザインシステム導入を通じて感じたことは、制限があることで創造性が損なわれるのではなく、むしろ明確なルールと基盤があるおかげで、本来解決すべき課題に集中できるということです。今回メルカリ ハロでデザインシステムを導入したことで、デザイナーとエンジニア間でとても効率的に開発を進めることができたと感じています。

参考: Figma Blog – デザインシステムの基本: デザインシステムとは?

メルカリ ハロのデザインシステム Componentのご紹介

  • Design Token
  • Components
  • Specific Components
    などのように大きく3つの構成要素で分かれています。

Design Token

Design Tokenとして定義しているのは5つあります。

  • Color
  • Space
  • Radius
  • Shadow
  • Typography

Colorに関してはDark Mode / Light Modeを定義しておりvariablesとして管理されています。

Figmaのデザインデータでは、Spacing、Radius、Typographyなども画面サイズに応じて変数で管理されています。ただし、ハロアプリはタブレットなどの大きな画面に対応していないため、これらは反映されていません。

Components

Componentsではドメインに依存しないようなUI Componentを定義しています。

Button / AppBar / Checkbox / Divider / Toggle / Badge / Tab / ProgressBar / Indicator / Snackbar / Tooltip など他のデザインシステムにもあるような一般的なものや

Input / Section Title / State Message / Callout / Notesなど、少しハロ独自で使っているがドメインに依存しすぎないようなComponentを用意しています。

Specific Components

Specific Componentsとしてハロのドメインや仕様に依存していて、複数画面で使用されるようなComponentも定義しています。

Specific Components自体も基本的にはComponentsの組み合わせでできており、それぞれのComponentが持っているプロパティはSpecific Componentでも適用することが可能です。

こちらのSpecific Componentsのケースで言うと内部でCalloutという上記のComponentとして定義したものを使用しているため、CalloutのComponentのプロパティで変更できる部分に関してはこちらでも変更することが可能です。

FigmaからFlutterへの実装について

Listのアイテムを例に説明していきます。
ハロ内ではListItemでも複数のパターンのitemを用意しています。
これを全て1つのComponentとしてプロパティを切り替えで定義してしまうと、プロパティがたくさん増え、いろんな組み合わせでいろんな表現ができる一方で、わかりにくいComponentになってしまいます。
ハロでは下記の様にそれぞれ別のComponentとして取り扱っています。

Figma上でコンポーネントのプロパティを✏️で示してもらうことで、変更可能な値がわかりやすくなり、実装時にはそのプロパティを基にしてコンポーネントを作成することで、デザインデータとコードの認識を一致させることができました。

Show Divider: Top / Show Divider: Bottomなど Figmaのデザイン上は便利な一方でだが実際に実装時にComponentには入れない部分などもありますが、おおよそ同じようなプロパティ構成になっています。

実際のComponentのコード

class ListItemWithValue extends ConsumerWidget {
  ListItemWithValue({
    required this.label,
    required this.value,
    required this.horizontalPadding,
    super.key,
    this.note,
    this.onTap,
    this.align = ListItemValueAlign.end,
    ...
  }) : assert(
          !((note?.isNotEmpty ?? false) && align == ListItemValueAlign.start),
          'Value align can only be start if note is not null',
        );
  final String label;
  final String value;
  final String? note;
  final GestureTapCallback? onTap;
  final ListItemValueAlign align;
...

}

Figmaのデータを元に実装する際にはFigmaのDev Modeをよく利用しています。Dev Modeを使用することでCSSやiOSのUIKit / SiwftUI、AndroidのXML / Composeなどのコードに変換された状態で確認することができます。残念ながら、現在Flutterは公式サポートされていないですが、Community pluginでFlutterのコードに変換するようなPluginがいくつかあり、そちらを使うことで実装を容易にすることができます。

Figma to Code (HTML, Tailwind, Flutter, SwiftUI)などのpluginを使って、変換されたコードを見ながら、Widgetの構成を検討することがあります。変換されたコードは高さや幅が固定値になってしまっていたり、無駄なWidgetがあったりするためそのまま使うのは難しいですが、実装時の参考になることは多いです。

Componentを確認する

前回 メルカリ ハロ アプリの技術スタックの紹介でもWidgetbookについても軽く触れていますが、Componentを確認するためにWidgetbookを使用しています。

※1: Figma上のプロパティ ※2: コード上のプロパティ

上記のように Figma上の ✏️ 部分(※1)とコードのプロパティ(※2)の認識を合わせることで、Componentで変更できる値がわかりやすくなり、コミュニケーションがとてもスムーズに行えるようになっています。
画面実装前のComponentを量産する時期には、新規画面実装の際のカタログとしてWidgetbookが大いに活躍していました。Webでよく使われているStorybookが持っているようなさまざまな機能などが今後Widgetbookにも追加されていけば、画面実装時のモックUI実装にも大いに役立つ可能性があり、今後もWidgetbookに期待しています。

Componentを使った画面実装

メルカリ ハロのデザインシステムを活用して、具体的な画面実装を行う方法について説明します。ここでは、設定画面を例に、どのようにデザインシステムのComponentを使用して画面を構築するかを紹介します。

設定画面の実装

設定画面では、以下のようにComponentを使用しています。

  • NavigationBar – 画面のタイトルと戻るボタンを表示
  • Notes – 設定に関する説明文を表示
  • ListItem – 現在の設定状態を表示
  • Button – 設定変更用のボタン
class SettingsScreen extends HookConsumerWidget {
  const SettingsScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final theme = ref.watch(appThemeProvider);

    return Scaffold(
      // 1. Navigation Bar - 画面のタイトルと戻るボタンを表示
      appBar: const HalloNavigationBar.normal(
        title: Text('設定画面タイトル'),
      ),
      body: SafeArea(
        child: Padding(
          padding: EdgeInsets.only(
            left: theme.spacing.size4,
            right: theme.spacing.size4,
            top: theme.spacing.size8,
            bottom: theme.spacing.size10,
          ),
          child: Column(
            children: [
              // 2. Notes - 設定に関する説明文を表示
              const HalloNotes(
                content: '設定に関する説明文をここに記載します',
              ),
              Gap(theme.spacing.size8),

              // 3. List Item - 現在の設定状態を表示
              HalloListItem.value(
                label: '設定項目名A',
                value: '設定状態',
              ),
              const HalloDivider(),

              HalloListItem.leadingWidget(
                label: '設定項目名B',
                leading: const Icon(Icons.info_outline_rounded),
                note: '設定項目に関するメモなどを記載する',
                doubleLine: true,
              ),
              const HalloDivider(),

              HalloListItem.trailingWidget(
                label: '設定項目名C',
                trailing: HalloSwitchButton(
                  value: true,
                  onChanged: (value) {
                    // Switchのオンオフによって実行される処理
                  },
                ),
                note: 'オンオフを設定することが可能',
              ),
              const HalloDivider(),

              const Spacer(),

              // 4. Button - 設定変更用のボタン
              HalloButton.filled(
                label: '設定を変更する',
                onPressed: () {
                  // 設定画面を開く処理
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

このように、デザインシステムのコンポーネントを使用することで、統一感のあるUIを簡単に実装することができます。各コンポーネントは再利用可能であり、デザインの一貫性を保ちながら効率的に開発を進めることができます。

リリースから半年後に見えてきた課題

メルカリ ハロは今年の3月にリリースされています。去年の今頃は上記のComponentを用いて各Component自体はWidgetbookで確認することができ、それらのComponentを使用して新規の画面を実装するフェーズでした。Componentの活用により、生産性高く画面実装を行うことができたと感じています。
最近は新しい画面を作ることもありますが、今まであった画面の機能の改善や機能を追加していくことも増えています。その中で既存のコンポーネントでは対応しきれないケースや、当初の設計では考慮されていなかった部分が少しずつ明らかになってきました。
例えばComponentに内包されているPaddingやMarginとは違うものを使った方が、単体の画面構成としては収まりが良いなど、既存のものとは少しだけ異なる亜種のComponentが生まれ始めています。ルールに縛られすぎてUXを阻害するものになってはいけないと思いつつも、デザインシステムは、ユーザー体験だけでなく、開発者やデザイナーの体験も高めることで、その効果を最大限に発揮できると考えています。
現在はデザイナーとコミュニケーションを取り、基本的にはデザインシステムで定義されているものを使用するというルールを改めて確認するようにしています。一方で、デザインシステムで定義されているものだけではユーザー体験を損ねる可能性があるケースにし関しては、デザインシステム自体の改善であったり、Specific Componentsを定義するような流れをとっています。

今後の展望

メルカリ ハロのデザインシステムは、さらなる開発の効率化と品質向上を目指して進化を続けています。この目的のために、いくつかの新しい可能性を模索しています。
まだアイデアの段階ではありますが、Figmaなどのデザインツールにおいて、Componentが正しく使用されていない箇所を自動的に検知できるlintの仕組みを導入することを検討しています。また、新たなチームメンバーが加わる際に、デザインシステムを円滑に理解し活用できるよう、勉強会やドキュメントの整備にも力を入れています。全員が共通の知識とスキルを持てる環境を作り、プロジェクトに貢献できるよう取り組んでいきます。

さらにFigma AIはじめv0bolt.newなど生成AIを使ったデザインおよび実装のアプローチに興味を持っており、これらを参考にメルカリ ハロのデザインシステムに基づいたデザインから実装の効率化の方法を模索しています。これによって、デザインを試行錯誤するプロセスが、デザイナーやエンジニアだけでなく、他の職種の方々にも手の届きやすいものになればと考えています。

まとめ

この記事では、メルカリ ハロのデザインシステム導入による開発効率向上とその具体的な運用方法についてご紹介しました。デザインシステムは、ビジュアルの一貫性を保つだけでなく、デザイナーとエンジニアが円滑に連携し、生産性を高めるための基盤となっています。リリースから半年以上が経過し、新たな課題も浮上していますが、引き続きデザイナーと協力してデザインシステムの改善を続けています。
今後は生成AIの活用を視野に入れつつ、さらなる効率化とデザインの品質向上を進め、より良いユーザー体験を提供できるよう努めていきます。

明日の記事はmshibuyaさんです。引き続きお楽しみください。

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加