【Flutter】Variables機能でFigma×Flutterのデザイントークンを実現する
記事の概要
本記事では、Figma2023で登場した「Variables機能」を利用してデザイントークンを実現し、FigmaAPI経由でFlutterアプリに連携する方法について解説する。
FigmaからFlutterアプリへのVariablesの連携には、コマンド一つで連携可能なfigma_vars_to_dartというパッケージを利用する。
※本記事で解説する内容はこちらのプラン一覧に記載されているREST API for variablesの利用権限が必要です。現時点(2023/9/25)で、利用権限があるのはEnterpriseアカウントのみで、その他のアカウントではAPI経由でVariableを連携できないため注意してください。
前提知識
デザイントークンとは
色の値、スペース、タイポグラフィのスケールなどに名前をつけて定義し、デザインシステムを構成する最小要素をデザイントークンと呼ぶ。
デザイナー、エンジニア間の連携を効率化できる方法論として注目されている。
詳しい解説はこちらの記事などを参考にする。
Variables機能とは
Variables機能はFigma2023で登場し、デザイントークンの実現には不可欠なエイリアスの定義をすることができる。
Figmaではデザイン要素に対してkey-valueの定義はできたが、エイリアスの定義ができず、プラグイン等なしにデザイントークンの実現はできなかった。
FigmaAPIとは
Figma上の様々なデータを取得、利用することのできるAPI。利用方法はこちらを参考にする。本記事ではVariablesを取得するためにVariables REST APIを利用する。
開発環境
iphone14
iOS v16.1
Flutter v3.13.4
実装手順
1. FigmaでVariableを定義
Figmaにログインし、デザインページで何も選択していない状態で、Local Variablesからデザイントークンを作成する。
2. Figma APIのアクセストークンを生成
FigmaAPIを利用するために必要なアクセストークンを生成する。
アカウントのSettings>Personal access tokensからトークンを生成し、保存しておく。
作成時に設定するScopesの説明はこちらのページのAuthentication>Scopesを参照。
今回は、Variablesの権限が必要となる。
3. ライブラリのインストール
dependencies:
figma_vars_to_dart: ^0.0.4
4. FigmaからFlutterへVariablesを連携
ターミナルから以下のコマンドを実行することでFigma上で定義したVariablesがFlutterアプリへ連携され、ファイルが生成される。
dart pub run figma_vars_to_dart generate \
--token $FIGMA_TOKEN \ ※1
--fileId $YOUR_FIGMA_FILE_ID \ ※2
--dartOutputFolder lib/shared/ui_constants \
--jsonOutputFile vars.json
※1
$FIGMA_TOKEN :
手順2で生成したアクセトークンをセットする。
※2
$YOUR_FIGMA_FILE_ID :
ブラウザでFigmaを開いている場合はデザインページのURLのfile/以降が$YOUR_FIGMA_FILE_IDとなる。
デスクトップアプリで開いている場合は、タブを右クリックし「リンクをコピー」を押せばURLが取得できるため上記と同様にFILE_IDを取得し、セットする。
コマンド実行後、以下のファイルがFlutterアプリ内に生成される。
color_primitives.dart
import 'package:flutter/widgets.dart';
class ColorPrimitives {
final Color white;
final Color pink;
final Color green;
final Color black;
final Color blue;
ColorPrimitives({
required this.white,
required this.pink,
required this.green,
required this.black,
required this.blue,
});
factory ColorPrimitives.value() => ColorPrimitives(
white: const Color(0xFFF2ECEC),
pink: const Color(0xFFDD006A),
green: const Color(0xFF8CC93E),
black: const Color(0xFF2C2C2C),
blue: const Color(0xFF3000F2),
);
}
color_semantics.dart
import 'package:flutter/widgets.dart';
import 'ui_constants.dart';
class ColorSemantics {
final Color background;
final Color buttonPrimary;
ColorSemantics({
required this.background,
required this.buttonPrimary,
});
factory ColorSemantics.light(ColorPrimitives colorPrimitives) =>
ColorSemantics(
background: colorPrimitives.white,
buttonPrimary: colorPrimitives.blue,
);
factory ColorSemantics.dark(ColorPrimitives colorPrimitives) =>
ColorSemantics(
background: colorPrimitives.black,
buttonPrimary: colorPrimitives.green,
);
}
ui_constants.dart
export 'color_primitives.dart';
export 'color_semantics.dart';
4. FlutterでVariablesを利用
import 'package:flutter/material.dart';
import 'shared/ui_constants/ui_constants.dart';
void main() {
runApp(const MyApp());
}
// ①ライトモード/ダークモードで切り替え
extension ColorsExt on BuildContext {
ColorSemantics get colors {
final primitives = ColorPrimitives.value();
if (MediaQuery.platformBrightnessOf(this) == Brightness.light) {
return ColorSemantics.light(primitives);
} else {
return ColorSemantics.dark(primitives);
}
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key, required this.title});
final String title;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// ②デザイントークンの利用
backgroundColor: context.colors.background,
// backgroundColor: context.colors.buttonPrimary,
title: Text(
title,
style: TextStyle(color: Colors.black),
),
),
body: null);
}
}
動作確認
上記main.dartの「②デザイントークンの利用」backgroundColorでデザイントークンの利用を確認できる。
・backgroundColor: context.colors.backgroundの場合
・backgroundColor: context.colors.buttonPrimaryの場合
参考
figma_vars_to_dart
Figma plans and features
デザイントークンって何?
Guide to variables in Figma
Figma API
Discussion