LoginSignup
16
7

More than 1 year has passed since last update.

【Jetpack Compose】スロットという考え方

Last updated at Posted at 2021-12-06

はじめに

本記事は with Advent Calendar 2021 6日目の記事です。

こんにちは with でAndroid エンジニアをしている石田です。withでは Jetpack Compose を段階的にプロダクトに導入しています。

本記事では、Jetpack Composeで知っていると便利なスロット(Slot)という考え方を紹介します。

スロットとは

スロット「投入口」 という意味です。Jetpack ComposeではUI部品を定義して共通化できるのはもちろんのこと、スロットによりUI部品の組み合わせ方を共通化することもできます。

例を見るのが早いと思うので、2つ紹介します。

例1: 注文のチェックアウト

こんな画面を作ってくださいと言われたとします。画面を観察すると、セクションごとに「左上のタイトル」、「右上のアクション」、「コンテンツ」の3つの部品の組み合わせで構成されていることが分かります。

画面 構造
slot.png

そこで、3つのスロットを考えます。

  • Title slot
  • Action slot
  • Content slot

これらのスロットに何をはめ込むかは呼び出し側の自由です。titleaction は省略できると便利なので デフォルトで空のコンポーザブル関数を渡しておきます。

@Composable
fun SectionTemplate(
    modifier: Modifier = Modifier,
    title: @Composable () -> Unit = {},
    action: @Composable () -> Unit = {},
    content: @Composable () -> Unit
) {
    Surface(
        color = MaterialTheme.colorScheme.secondaryContainer,
        contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
        shape = RoundedCornerShape(32.dp),
    ) {
        Column(
            modifier = Modifier
                .padding(horizontal = 24.dp, vertical = 16.dp)
                .then(modifier),
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            Row(
                horizontalArrangement = Arrangement.spacedBy(16.dp),
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Box(
                    modifier = Modifier.weight(1f),
                ) {
                    ProvideTextStyle(
                        MaterialTheme.typography.titleLarge,
                    ) {
                        title()
                    }
                }
                action()
            }
            content()
        }
    }

この SectionTemplate を使用して CartSection と PaymentSection を作成します。

@Composable
fun CartSection() {
    SectionTemplate(
        title = {
            Text(text = "Cart")
        },
        action = {
            TextButton(onClick = {}) {
                Text(text = "See all")
            }
        }
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            IntRange(1, 6).forEach {
                Text(text = "Item $it")
            }
            Text(text = "...")
        }
    }
}
@Composable
fun PaymentSection() {
    SectionTemplate(
        title = {
            Text(text = "Payment")
        },
        action = {
            IconButton(onClick = {}) {
                Icon(
                    imageVector = Icons.Filled.Info,
                    contentDescription = null,
                )
            }
        }
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            Text(text = "XXXX-XXXX-XXXX")
            Text(text = "XX/XX")
            Text(text = "XXX")
        }
    }
}

例2: Scaffold

実は Scaffold にも以下のようなスロットが用意されていて、自由に中身をはめ込めるようになっています。

  • Top Bar slot
  • Bottom Bar slot
  • Floating Action Button slot
  • Drawer Content slot
  • Content slot
@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    topBar: @Composable () -> Unit = {},
    bottomBar: @Composable () -> Unit = {},
    floatingActionButton: @Composable () -> Unit = {},
    drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
    ..  ..
    content: @Composable (PaddingValues) -> Unit
) {
  ..  ..
}

引用元:

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/src/commonMain/kotlin/androidx/compose/material/Scaffold.kt;l=158-177?q=scaffold.kt

まとめ

  • ComposeではUI部品を共通化できるだけでなく、スロットによりUI部品の組み合わせ方を共通化することもできます。
  • スロットは抽象度が高めになりますが、強力な考え方になるので覚えておくとよいです。
  • 動作可能なサンプルコードをGitHubに公開しているので、併せてご参照ください。
16
7
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
16
7