Relay と Jetpack Compose を使ってアプリ全体を構築する

1. 始める前に

Relay は、Figma で UI コンポーネントを設計し、それを Jetpack Compose プロジェクトで直接使用するためのツールキットです。煩雑なデザイン仕様や QA サイクルが不要であるため、優れた Android UI をすばやく提供できるようになります。

この Codelab では、Relay UI パッケージを Compose 開発プロセスに統合する方法を学びます。これは統合手法に重点を置いたものであり、開始から完了までのワークフローを説明するものではありません。Relay の一般的なワークフローについては、Relay の基本チュートリアルをご覧ください。

前提条件

  • Compose に関する基本的な経験。基本的な経験がない場合には、Jetpack Compose の基本の Codelab を受講してください。
  • Kotlin の構文に関する経験。

学習内容

  • UI パッケージをインポートする方法。
  • UI パッケージをナビゲーションとデータ アーキテクチャに統合する方法。
  • UI パッケージをコントローラのロジックでラップする方法。
  • Figma スタイルを Compose テーマにマッピングする方法。
  • 生成されたコードで UI パッケージを既存のコンポーザブルに置き換える方法。

作成するアプリの概要

  • デザイナーが提供する Relay パッケージに基づき、現実的なアプリデザインを実装します。作成するアプリは Reflect という毎日のマインドフルネスと健康的な習慣をトラッキングするアプリです。これにはさまざまなタイプのトラッカーが含まれており、またトラッカーの追加、管理のための UI が用意されています。アプリは次のようなものです。

完成したアプリ

必要なもの

2. 設定する

コードを取得する

この Codelab のコードを取得するには、以下のいずれかを実行します。

$ git clone https://github.com/googlecodelabs/relay-codelabs
  • GitHub の relay-codelabs リポジトリに移動し、対象のブランチを選択します。[Code] > [Download zip] をクリックして、ダウンロードした ZIP ファイルを展開します。

どちらの方法でも、main ブランチにはスターター コードが含まれ、end ブランチには解答コードが含まれています。

Relay for Android Studio プラグインをインストールする

Relay for Android Studio プラグインをまだインストールしていない場合には、次の手順でインストールします。

  1. Android Studio で [Settings] > [Plugins] をクリックします。
  2. テキスト ボックスに「Relay for Android Studio」と入力します。
  3. 検索結果に表示される拡張機能で、[Install] をクリックします。

Android Studio のプラグイン設定

  1. [Third-party plugins privacy note] ダイアログが表示された場合は、[Accept] をクリックします。
  2. [OK] > [Restart] をクリックします。
  3. [Confirm exit] ダイアログが表示されたら、[Exit] をクリックします。

Android Studio と Figma を連携させる

Relay は Figma API を使って UI パッケージを取得します。Figma を使用するには、無料の Figma アカウント個人用アクセス トークンが必要です。これらは「必要なもの」セクションでも挙げられています。

まだ Android Studio と Figma が連携していない場合は、次の手順に沿って連携させます。

  1. Figma アカウントで、ページの一番上にあるプロフィール アイコンをクリックし、[Settings] を選択します。
  2. [Personal access tokens] セクションで、テキスト ボックスにトークンの説明を記入し、Enter(macOS の場合は return)を押すと、トークンが生成されます。
  3. [Copy this token] をクリックします。

Figma で生成されたアクセス トークン

  1. Android Studio で [Tools] > [Relay Settings] をクリックします。[Relay settings] ダイアログが開きます。
  2. [Figma Access Token] テキスト ボックスにアクセス トークンを貼り付け、[OK] をクリックします。これで環境が設定されました。

3. アプリのデザインを確認する

Reflect アプリでは、デザイナーと協力してアプリのカラー、タイポグラフィ、レイアウト、動作を定義しています。マテリアル デザイン 3 規則に沿ってデザインを行い、アプリがマテリアル コンポーネントやテーマとシームレスに連携するようにしています。

ホーム画面を確認する

ホーム画面にはユーザーが選択したトラッカーのリストが表示されます。アクティブな日付を変更し、他のトラッカーを作成するためのアフォーダンスも用意されています。

ホーム画面

デザイナーは Figma でこの画面を複数のコンポーネントに分割し、API を定義して、Relay for Figma プラグインによってパッケージ化しました。コンポーネントをパッケージ化すると、Android Studio プロジェクトにインポートできるようになります。

ホーム画面コンポーネント

追加と編集画面を確認する

追加と編集画面で、ユーザーはトラッカーを追加または編集できます。表示されるフォームはトラッカーのタイプによって若干異なります。

追加と編集画面

同様に、この画面も複数のパッケージ化されたコンポーネントに分割されています。

追加と編集画面コンポーネント

テーマを確認する

このデザインのカラーとタイポグラフィは、マテリアル デザイン 3 のトークン名に基づき、Figma スタイルとして実装されています。これにより、Compose テーマとマテリアル コンポーネントの高い相互運用性を確保できます。

Figma スタイル

4. UI パッケージをインポートする

UI パッケージをプロジェクトにインポートする前に、デザインソースを Figma にアップロードする必要があります。

Figma ソースのリンクを取得する手順は次のとおりです。

  1. Figma で [Import file] をクリックして、Codelab プロジェクト フォルダにある ReflectDesign.fig ファイルを選択します。
  2. ファイルを右クリックして、[Copy link] を選択します。コピーしたリンクは次のセクションで必要になります。

a98d24b4d5ee5c34.png

UI パッケージをプロジェクトにインポートする

  1. Android Studio で ./CompleteAppCodelab プロジェクトを開きます。
  2. [File] > [New] > [Import UI Packages] をクリックします。[Import UI Packages] ダイアログが開きます。
  3. [Figma source URL] テキスト ボックスに、先ほどのセクションでコピーした URL を貼り付けます。

[Import UI Packages] ダイアログ

  1. [App theme] テキスト ボックスに「com.google.relay.example.reflect.ui.theme.ReflectTheme」と入力します。これにより、カスタムテーマを使用したプレビューが生成されます。
  2. [Next] をクリックします。ファイルの UI パッケージのプレビューが表示されます。
  3. [Create] をクリックします。パッケージがプロジェクトにインポートされます。
  4. [Project] タブに移動し、ui-packages フォルダの横にある展開矢印 2158ffa7379d2b2e.png をクリックします。

ui-packages フォルダ

  1. いずれかのパッケージ フォルダの横にある展開矢印 2158ffa7379d2b2e.png をクリックすると、JSON ソースファイルとアセットの依存関係が含まれていることがわかります。
  2. JSON ソースファイルを開きます。Relay モジュールにパッケージのプレビューと API が表示されます。

Relay パッケージ プレビュー モジュール

コードを構築し生成する

  1. Android Studio の一番上にある b3bc77f3c78cac1b.png Make project アイコンをクリックします。各パッケージの生成されたコードが java/com.google.relay.example.reflect ファイルに追加されます。生成されたコンポーザブルには、Figma デザインのすべてのレイアウトとスタイル設定情報が含まれています。
  2. 必要に応じて [Split] をクリックすると、コードとプレビュー ペインを横に並べて表示できます。
  3. range/Range.kt ファイルを開くと、Compose プレビューがコンポーネントのバリエーションごとに作成されていることがわかります。

c0d21ab0622ad550.png

5. コンポーネントを統合する

このセクションでは、生成された Switch トラッカーのコードを詳しく確認します。

Switch トラッカーのデザイン

  1. Android Studio で com/google/relay/example/reflect/switch/Switch.kt ファイルを開きます。

Switch.kt(生成されたコード)

/**
 * This composable was generated from the switch UI Package.
 * Generated code; don't edit directly.
 */
@Composable
fun Switch(
    modifier: Modifier = Modifier,
    isChecked: Boolean = false,
    isPressed: Boolean = false,
    emoji: String = "",
    title: String = ""
) {
    TopLevel(modifier = modifier) {
        if (isChecked) {
            ActiveOverlay(modifier = Modifier.rowWeight(1.0f).columnWeight(1.0f)) {}
        }
        if (isPressed) {
            State(modifier = Modifier.rowWeight(1.0f).columnWeight(1.0f)) {}
        }
        TopLevelSynth {
            Label(modifier = Modifier.rowWeight(1.0f)) {
                Emoji(emoji = emoji)
                Title(
                    title = title,
                    modifier = Modifier.rowWeight(1.0f)
                )
            }
            if (isChecked) {
                Checkmark {
                    Vector(modifier = Modifier.rowWeight(1.0f).columnWeight(1.0f))
                }
            }
        }
    }
}
  1. 次の点に注意してください。
  • Figma デザインのすべてのレイアウトとスタイルが生成されています。
  • サブコンポーネントは個別のコンポーザブルに分割されています。
  • コンポーザブル プレビューがすべてのデザイン バリエーションで生成されています。
  • カラーとタイポグラフィのスタイルはハードコードされています。これは後で修正できます。

トラッカーを挿入する

  1. Android Studio で java/com/google/relay/example/reflect/ui/components/TrackerControl.kt ファイルを開きます。このファイルは習慣トラッカーにデータと操作ロジックを提供します。現時点では、このコンポーネントはトラッカー モデルからの元データを出力しています。

7850337c9ba23fd5.png

  1. com.google.relay.example.reflect.switch.Switch パッケージをファイルにインポートします。
  2. trackerData.tracker.type フィールドにピボットされた when ブロックを作成します。
  3. when ブロックの本文で、タイプが TrackerType.BOOLEAN のときに Switch() Composable 関数を呼び出します。

コードの内容は次のようになります。

TrackerControl.kt

when (trackerData.tracker.type) {
    TrackerType.BOOLEAN ->
        Switch(
          title = trackerData.tracker.name,
          emoji = trackerData.tracker.emoji
        )
    else ->
        Text(trackerData.tracker.toString())
}
  1. プロジェクトを再ビルドします。これにより、ライブデータでデザインしたとおりに、トップページに正しく Switch トラッカーが表示されるようになります。

f07eda1a7740129b.png

6. 状態と操作を追加する

UI パッケージはステートレスです。レンダリングされるものは、渡されたパラメータのシンプルな結果です。一方、実際のアプリでは操作と状態が必要です。他のパラメータのようにインタラクション ハンドラを生成されたコンポーザブルに渡すことはできますが、このハンドラが操作する状態はどこに保持すればよいでしょうか。同じハンドラをすべてのインスタンスに渡さないようにするにはどうすればよいでしょうか。パッケージの構成を再利用可能なコンポーザブルに抽象化するにはどうすればよいでしょうか。このような場合には、生成されたパッケージをカスタムの Composable 関数でラップすることをおすすめします。

コントローラ Composable 関数で UI パッケージをラップする

コントローラ Composable 関数で UI パッケージをラップすると、表示またはビジネス ロジックをカスタマイズでき、必要な場合にはローカルの状態を管理することもできます。デザイナーは引き続きオリジナルの UI パッケージを Figma でアップデートできます。ラッパーコードをアップデートする必要はありません。

Switch トラッカーのコントローラを作成する手順は以下のとおりです。

  1. Android Studio で java/com/google/relay/example/reflect/ui/components/SwitchControl.kt ファイルを開きます。
  2. SwitchControl() Composable 関数で、次のパラメータを渡します。
  • trackerData: TrackerData オブジェクト
  • modifier: デコレータ オブジェクト
  • onLongClick: トラッカーの長押しで編集と削除ができるインタラクション コールバック

modifier

  1. combinedClickable 修飾子を Switch() 関数に渡し、クリックと長押しを処理します。
  2. TrackerData オブジェクトの値を isToggled() メソッドも含め、Switch() 関数に渡します。

完成した SwitchControl() 関数は次のコード スニペットのようになります。

SwitchControl.kt

package com.google.relay.example.reflect.ui.components

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.relay.example.reflect.model.Tracker
import com.google.relay.example.reflect.model.TrackerData
import com.google.relay.example.reflect.model.TrackerType
import com.google.relay.example.reflect.switch.Switch

/*
 * A component for controlling switch-type trackers.
 *
 * SwitchControl is responsible for providing interaction and state management to the stateless
 * composable [Switch] generated by Relay. [onLongClick] provides a way for callers to supplement
 * the control's intrinsic interactions with, for example, a context menu.
 */
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SwitchControl(
    trackerData: TrackerData,
    modifier: Modifier = Modifier,
    onLongClick: (() -> Unit)? = null,
) {
    Switch(
        modifier
            .height(64.dp)
            .clip(shape = RoundedCornerShape(size = 32.dp))
            .combinedClickable(onLongClick = onLongClick) {
                trackerData.toggle()
            },
        emoji = trackerData.tracker.emoji,
        title = trackerData.tracker.name,
        isChecked = trackerData.isToggled(),
    )
}

@Preview
@Composable
fun SwitchControllerPreview() {
    val data = TrackerData(
        Tracker(
            emoji = "🍕",
            name = "Ate Pizza",
            type = TrackerType.BOOLEAN
        )
    )
    SwitchControl(data)
}
  1. TrackerControl.kt ファイルで、Switch インポートを削除し、Switch() 関数を SwitchControl() 関数の呼び出しに置き換えます。
  2. TrackerType.RANGETrackerType.COUNT 列挙定数のケースを追加します。

完成した when ブロックは次のコード スニペットのようになります。

SwitchControl.kt

when (trackerData.tracker.type) {
    TrackerType.BOOLEAN ->
        SwitchControl(
            trackerData = trackerData,
            onLongClick = { expanded = true },
        )
    TrackerType.RANGE ->
        RangeControl(
            trackerData = trackerData,
            onLongClick = { expanded = true },
        )
    TrackerType.COUNT ->
        ValueControl(
            trackerData = trackerData,
            onLongClick = { expanded = true },
        )
}
  1. プロジェクトを再ビルドします。トラッカーが表示され、操作できるようになりました。これでホーム画面の完成です。

b23b94f0034243d3.png

7. 既存のコンポーネントをマッピングする

Relay では、デベロッパーは UI パッケージを既存のコンポーザブルに置き換えることによって、生成されたコードをカスタマイズできます。この方法を使用すると、すぐに使用できるコンポーネント、さらにはコードのカスタム デザイン システムを出力できます。

テキスト フィールドをマッピングする

次の画像は [Add/edit tracker​STRONG] ダイアログの Switch Tracker Editor のデザインです。

Switch 設定コンポーネントのデザイン

デザイナーはデザインに ReflectTextField を使用しました。これはすでにマテリアル デザイン 3 のテキスト フィールドの上に構築されたコードに実装しています。Figma はネイティブにはテキスト フィールドに対応していないため、Relay で生成されたデフォルト コードはデザインのみであり、コントロールとしては機能しません。

この要素の実際の実装に変更するには、テキスト フィールドの UI パッケージとファイルのマッピングの 2 つが必要となります。デザイナーはすでに Figma のデザイン システム コンポーネントをパッケージ化し、Tracker Editor のデザインにボタン コンポーネントを使用しています。デフォルトでは、このネストされたパッケージは設定バーパッケージの依存関係として生成されますが、切り替えにはコンポーネント マッピングを使用します。

テキスト フィールドの Figma コンポーネントに重ねて表示された Relay プラグイン

マッピング ファイルを作成する

Relay for Android Studio プラグインには、コンポーネント マッピング ファイルをすばやく作成する機能があります。

マッピング ファイルを作成する手順は次のとおりです。

  1. Android Studio で text_field UI パッケージを右クリックして、[Generate mapping file] を選択します。

[Generate mapping file] コンテキスト メニュー項目

  1. ファイルに次のコードを入力します。

text_field.json

{
  "target": "ReflectTextField",
  "package": "com.google.relay.example.reflect.ui.components",
  "generatePreviews": false
}

Component-mapping ファイルは Compose クラス ターゲットとパッケージ、任意の fieldMapping オブジェクト一式を指定します。このフィールド マッピングによって、パッケージ パラメータを該当する Compose パラメータに変換できます。このケースでは API はそれぞれ同じであるため、指定する必要があるのはターゲット クラスだけです。

  1. プロジェクトを再ビルドします。
  2. trackersettings/ TrackerSettings.kt ファイルで、生成されたコンポーズ可能な関数 TitleFieldStyleFilledStateEnabledTextConfigurationsInputText() を確認すると、生成された ReflectTextField コンポーネントが含まれていることがわかります。

TrackerSettings.kt(生成されたコード)

@Composable
fun TitleFieldStyleFilledStateEnabledTextConfigurationsInputText(
    onTitleChanged: (String) -> Unit,
    title: String,
    modifier: Modifier = Modifier
) {
    ReflectTextField(
        onChange = onTitleChanged,
        labelText = "Title",
        leadingIcon = "search",
        trailingIcon = "cancel",
        supportingText = "Supporting text",
        inputText = title,
        state = State.Enabled,
        textConfigurations = TextConfigurations.InputText,
        modifier = modifier.requiredHeight(56.0.dp)
    )
}

8. Compose テーマにマッピングする

デフォルトでは、Relay はカラーとタイポグラフィのリテラル値を生成します。これによって正確に変換されることを徹底できますが、コンポーネントは Compose テーマ設定システムを使用できなくなります。アプリをダークモードで表示すると、問題がよくわかります。

カラーが不適切であることを表しているダークモードでのホーム画面のプレビュー

日付のナビゲーション コンポーネントはほとんど読み取れず、カラーが適切ではありません。この問題を解決するには、Relay でスタイル マッピング機能を使って、Figma スタイルを生成されたコードの Compose テーマトークンにリンクします。これにより、Relay とマテリアル デザイン 3 コンポーネント間の視覚的な一貫性が高まり、ダークモードに対応できるようになります。

1fac916db14929bb.png

スタイル マッピング ファイルを作成する

  1. Android Studio で src/main/ui-package-resources/style-mappings フォルダに移動し、次のコードを含む figma_styles.json ファイルを作成します。

figma_styles.json

{
  "figma": {
    "colors": {
      "Reflect Light/background": "md.sys.color.background",
      "Reflect Dark/background": "md.sys.color.background",
      "Reflect Light/on-background": "md.sys.color.on-background",
      "Reflect Dark/on-background": "md.sys.color.on-background",
      "Reflect Light/surface": "md.sys.color.surface",
      "Reflect Dark/surface": "md.sys.color.surface",
      "Reflect Light/on-surface": "md.sys.color.on-surface",
      "Reflect Dark/on-surface": "md.sys.color.on-surface",
      "Reflect Light/surface-variant": "md.sys.color.surface-variant",
      "Reflect Dark/surface-variant": "md.sys.color.surface-variant",
      "Reflect Light/on-surface-variant": "md.sys.color.on-surface-variant",
      "Reflect Dark/on-surface-variant": "md.sys.color.on-surface-variant",
      "Reflect Light/primary": "md.sys.color.primary",
      "Reflect Dark/primary": "md.sys.color.primary",
      "Reflect Light/on-primary": "md.sys.color.on-primary",
      "Reflect Dark/on-primary": "md.sys.color.on-primary",
      "Reflect Light/primary-container": "md.sys.color.primary-container",
      "Reflect Dark/primary-container": "md.sys.color.primary-container",
      "Reflect Light/on-primary-container": "md.sys.color.on-primary-container",
      "Reflect Dark/on-primary-container": "md.sys.color.on-primary-container",
      "Reflect Light/secondary-container": "md.sys.color.secondary-container",
      "Reflect Dark/secondary-container": "md.sys.color.secondary-container",
      "Reflect Light/on-secondary-container": "md.sys.color.on-secondary-container",
      "Reflect Dark/on-secondary-container": "md.sys.color.on-secondary-container",
      "Reflect Light/outline": "md.sys.color.outline",
      "Reflect Dark/outline": "md.sys.color.outline",
      "Reflect Light/error": "md.sys.color.error",
      "Reflect Dark/error": "md.sys.color.error"
    },
    "typography": {
      "symbols": {
        "Reflect/headline/large": "md.sys.typescale.headline-large",
        "Reflect/headline/medium": "md.sys.typescale.headline-medium",
        "Reflect/headline/small": "md.sys.typescale.headline-small",
        "Reflect/title/large": "md.sys.typescale.title-large",
        "Reflect/title/medium": "md.sys.typescale.title-medium",
        "Reflect/title/small": "md.sys.typescale.title-small",
        "Reflect/body/large": "md.sys.typescale.body-large",
        "Reflect/body/medium": "md.sys.typescale.body-medium",
        "Reflect/body/small": "md.sys.typescale.body-small",
        "Reflect/label/large": "md.sys.typescale.label-large",
        "Reflect/label/medium": "md.sys.typescale.label-medium",
        "Reflect/label/small": "md.sys.typescale.label-small"
      },
      "subproperties": {
        "fontFamily": "font",
        "fontWeight": "weight",
        "fontSize": "size",
        "letterSpacing": "tracking",
        "lineHeightPx": "line-height"
      }
    }
  },
  "compose": {
    "colors": {
      "md.sys.color.background": "MaterialTheme.colorScheme.background",
      "md.sys.color.error": "MaterialTheme.colorScheme.error",
      "md.sys.color.error-container": "MaterialTheme.colorScheme.errorContainer",
      "md.sys.color.inverse-on-surface": "MaterialTheme.colorScheme.inverseOnSurface",
      "md.sys.color.inverse-surface": "MaterialTheme.colorScheme.inverseSurface",
      "md.sys.color.on-background": "MaterialTheme.colorScheme.onBackground",
      "md.sys.color.on-error": "MaterialTheme.colorScheme.onError",
      "md.sys.color.on-error-container": "MaterialTheme.colorScheme.onErrorContainer",
      "md.sys.color.on-primary": "MaterialTheme.colorScheme.onPrimary",
      "md.sys.color.on-primary-container": "MaterialTheme.colorScheme.onPrimaryContainer",
      "md.sys.color.on-secondary": "MaterialTheme.colorScheme.onSecondary",
      "md.sys.color.on-secondary-container": "MaterialTheme.colorScheme.onSecondaryContainer",
      "md.sys.color.on-surface": "MaterialTheme.colorScheme.onSurface",
      "md.sys.color.on-surface-variant": "MaterialTheme.colorScheme.onSurfaceVariant",
      "md.sys.color.on-tertiary": "MaterialTheme.colorScheme.onTertiary",
      "md.sys.color.on-tertiary-container": "MaterialTheme.colorScheme.onTertiaryContainer",
      "md.sys.color.outline": "MaterialTheme.colorScheme.outline",
      "md.sys.color.primary": "MaterialTheme.colorScheme.primary",
      "md.sys.color.primary-container": "MaterialTheme.colorScheme.primaryContainer",
      "md.sys.color.secondary": "MaterialTheme.colorScheme.secondary",
      "md.sys.color.secondary-container": "MaterialTheme.colorScheme.secondaryContainer",
      "md.sys.color.surface": "MaterialTheme.colorScheme.surface",
      "md.sys.color.surface-variant": "MaterialTheme.colorScheme.surfaceVariant",
      "md.sys.color.tertiary": "MaterialTheme.colorScheme.tertiary",
      "md.sys.color.tertiary-container": "MaterialTheme.colorScheme.tertiaryContainer"
    },
    "typography": {
      "symbols": {
        "md.sys.typescale.display-large": "MaterialTheme.typography.displayLarge",
        "md.sys.typescale.display-medium": "MaterialTheme.typography.displayMedium",
        "md.sys.typescale.display-small": "MaterialTheme.typography.displaySmall",
        "md.sys.typescale.headline-large": "MaterialTheme.typography.headlineLarge",
        "md.sys.typescale.headline-medium": "MaterialTheme.typography.headlineMedium",
        "md.sys.typescale.headline-small": "MaterialTheme.typography.headlineSmall",
        "md.sys.typescale.title-large": "MaterialTheme.typography.titleLarge",
        "md.sys.typescale.title-medium": "MaterialTheme.typography.titleMedium",
        "md.sys.typescale.title-small": "MaterialTheme.typography.titleSmall",
        "md.sys.typescale.body-large": "MaterialTheme.typography.bodyLarge",
        "md.sys.typescale.body-medium": "MaterialTheme.typography.bodyMedium",
        "md.sys.typescale.body-small": "MaterialTheme.typography.bodySmall",
        "md.sys.typescale.label-large": "MaterialTheme.typography.labelLarge",
        "md.sys.typescale.label-medium": "MaterialTheme.typography.labelMedium",
        "md.sys.typescale.label-small": "MaterialTheme.typography.labelSmall"
      },
      "subproperties": {
        "font": "fontFamily",
        "weight": "fontWeight",
        "size": "fontSize",
        "tracking": "letterSpacing",
        "line-height": "lineHeight"
      }
    },
    "options": {
      "packages": {
        "MaterialTheme": "androidx.compose.material3"
      }
    }
  }
}

テーマ マッピング ファイルは 2 種類の高レベルのオブジェクト、figmacompose で構成されています。これらのオブジェクト内で、カラーとタイプの定義が中間トークン経由で両方の環境間でリンクされます。これにより、複数の Figma スタイルを単一の Compose テーマエントリにマッピングできるため、ライトモードとダークモードに対応する際に役立ちます。

  1. マッピング ファイルを確認します。特に Figma のタイポグラフィ プロパティがどのように Compose の対象に再マッピングされているかを確認します。

UI パッケージを再インポートする

マッピング ファイル作成後に、すべての UI パッケージをプロジェクトに再インポートする必要があります。マッピング ファイルがなかったため、すべての Figma スタイル値が最初のインポート時に破棄されているからです。

UI パッケージを再インポートする手順は以下のとおりです。

  1. Android Studio で [File] > [New] > [Import UI Packages] をクリックします。[Import UI Packages] ダイアログが開きます。
  2. [Figma source URL] テキスト ボックスに、Figma ソースファイルの URL を入力します。
  3. [Translate Figma styles to Compose theme] チェックボックスをオンにします。
  4. [Next] をクリックします。ファイルの UI パッケージのプレビューが表示されます。
  5. [Create] をクリックします。パッケージがプロジェクトにインポートされます。

[Import UI Packages] ダイアログ

  1. プロジェクトを再ビルドして、switch/Switch.kt ファイルを開き、生成されたコードを表示します。

Switch.kt(生成されたコード)

@Composable
fun ActiveOverlay(
    modifier: Modifier = Modifier,
    content: @Composable RelayContainerScope.() -> Unit
) {
    RelayContainer(
        backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
        isStructured = false,
        radius = 32.0,
        content = content,
        modifier = modifier.fillMaxWidth(1.0f).fillMaxHeight(1.0f)
    )
}
  1. backgroundColor パラメータが Compose テーマ オブジェクトの MaterialTheme.colorScheme.surfaceVariant フィールドに設定されていることがわかります。
  2. プレビュー ペインでアプリをダークモードに切り替えると、テーマが適切に適用され、視覚的なバグが解消されています。

6cf2aa19fabee292.png

9. 完了

これで、Relay を Compose アプリに統合する方法を学びました。

詳細