見出し画像

Jetpack Composeのコンポーザブル使用法まとめ

皆さん、こんにちは!又はこんばんは!初めての方は初めまして!
Androidアプリに関する記事を書いている「りおん」です。
今回は、Jetpack Composeのコンポーザブルの使用法をまとめた記事となります。コンポーザブルの使い方をある程度網羅しているため、このページだけで基本的なことはできると思います。
また、実際に試せるようにコード例も載せていますので必要に応じて使用してください。
内容が非常に多いため、必要な部分のみを見ることをお勧めします。また、需要があれば今後これを単元ごとに分割した記事も投稿する予定です。
注意点として、まとめはしましたが、全てをまとることは到底できていません私がよく使用するコンポーザブルについてのみ書いています)。この記事に載っている方法でうまくいかない場合は、記事に載っていることをキーワードにして調べる(キーワード検索、chatGPT…)、又は見出しの下に添付された公式レファレンスで調べると目的のものが早く見つかると思います。
使用している言語はKotlinである点も気を付けてください。


コード例の使用方法

コンポーザブルの使い方が分かるように各コンポーザブルごとにコード例を記載しています。
このコード例の使用方法は、
1.Android Studioを開く
2.[File] -> [New] -> [New Project]で新しいプロジェクトを作成する
3.[Empty Activity]を選び[Next]、分かりやすいアプリ名を付けて[Finish]
4.実行し、画面にHello Android!が表示されることを確認
5.MainActivity.kt内のsetContentにあるGreetingコンポーザブルをExampleコンポーザブルに置き換える(Scaffoldを使用している場合はそれも消去します)
6.試したいコード例をMainActivityの下にコピー&ペーストする

※画面はversion Koalaのものです

レイアウトに関するコンポーザブル(Column, Row, Box)の共通点

ここでは、レイアウト(要素を配置する際のルール)に関するコンポーザブルとしてColumn、Row、Boxを紹介します。
このレイアウトに関するコンポーザブルには共通している拡張関数があるため、よく使用するものをまとめて紹介します。
Modifierの全ての拡張関数は下のサイトに載っています。

サイズを指定する

親のコンポーザブルと同じ大きさにする場合は、Modifierの拡張関数(Modifier.で表記される)のfillMaxHeight() 、fillMaxWidth()を使用します。両方使用する際はfillMaxSize()を使用します。
縦と横を指定したサイズにしたい場合はそれぞれheight()、width()を使用します。

色を塗る

コンポーザブルに色を塗るにはbackground()を使用します。
※注意点としてAndroid Studioのバージョンが古い(version Giraffe等)と、親となるコンポーザブルがない場合はColumnのサイズが画面サイズより小さくても画面全てに色がつくことがあります。その場合は、コメントアウトを解除し、色を付けるColumnの親にColumnを使用してください。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    //実行した際に画面全体が緑色になる場合はコメントアウトを解除してください
//    Column(
//        modifier = Modifier
//    ) {
        Column(
            modifier = Modifier
                .padding(top = 40.dp, start = 30.dp)
                .background(Color.Green)
                .height(500.dp)
                .width(200.dp)
        ){
            Text(
                text = "要素1",
                fontSize = 30.sp,
                modifier = modifier
            )
        }
//    }
}

スペースを取る

コンポーザブルの開始地点を親からスペースを取った地点から始める場合はpadding()を使用します。
左がstart、右がend、上がtop、下がendです。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        Column(
            modifier = Modifier
                .padding(start = 10.dp, top = 40.dp)
                .background(Color.Green)
                .height(100.dp)
                .width(200.dp)

        ){
            Text(
                text = "要素1",
            )
        }
    }
}

枠線を描く

枠線を書きたい場合はborder()を使用します。
※この枠線は外側ではなく内側に引かれることに注意してください。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Box(
            modifier = modifier
                .height(200.dp)
                .width(300.dp)
                .background(Color.Gray)
                .border(
                    border = BorderStroke(5.dp, Color.Black)
                )
        ) {
        }
    }
}

クリックできるようにする

クリックできるようにしたい場合は、clickable()を使用します。
クリックした際の挙動は { }の中に記述します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Box(
            modifier = modifier
                .height(200.dp)
                .width(300.dp)
                .background(Color.Gray)
                .clickable {  }
        ) {
        }
    }
}

はみ出した要素を表示しない

ほとんど使用しないと思いますが、タッチで移動できる要素をある枠組みの外に表示したくない場合があります。その時はclipToBounds()を使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var offset by remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState{ _, offsetChange, _ ->
        offset += offsetChange
    }

    Box {
        Box(
            modifier = Modifier
                .height(200.dp)
                .width(300.dp)
                .background(Color.Gray)
                .clipToBounds()
        ) {
            Box(
                modifier = Modifier
                    .height(60.dp)
                    .width(60.dp)
                    .graphicsLayer(
                        translationX = offset.x,
                        translationY = offset.y
                    )
                    .transformable(state = state)
                    .border(
                        BorderStroke(6.dp, Color.Black)
                    )
                    .background(Color.Red),
            )
        }
    }
}

スクロール可能にする

コンポーザブルをスクロール可能にするためには、縦スクロールならverticalScroll()、横スクロールなら horizontalScroll()を用います。
アイテムが多い場合はパフォーマンス的にもLazyColumnまたはLazyRowを使いましょう。
※注意点として、Android Studioのバージョンが古い(version Giraffeなど)と親となるコンポーザブルがない場合はサイズを指定しても反映されないことがあります。一方親がいる場合はサイズ指定が有効です。そのため、対策として親となるコンポーザブルを作成してサイズを指定することが挙げられます。

//ふつうはこちらで正常に実行可能
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column(
        modifier = modifier
            .height(200.dp)
            .verticalScroll(rememberScrollState())
    ) {
        repeat(50) {
            Text(text = "要素${it + 1}")
        }
    }
}
//Android Studioが古い場合のみ サイズ対策あり
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        Column(
            modifier = modifier
                .height(200.dp)
                .verticalScroll(rememberScrollState())
        ) {
            repeat(50) {
                Text(text = "要素${it + 1}")
            }
        }
    }
}

サイズ変更のアニメーションの追加

アニメーションの使用はアプリを視覚的により魅力のあるものに昇華させます。例えば、リストアイテムの展開アニメーションがある場合とない場合とでは印象が変わります。
ここでは、アニメーション修飾子であるanimateContentSize()拡張関数を使用して、サイズ変更のアニメーションを追加します。
animateContentSize()を使用することでサイズの変化が視覚的にスムーズになります。
※アイテムの出現と消失のアニメーションはAnimatedVisibilityコンポーザブル、異なるコンポーザブル間でアニメーション化する場合はAnimatedCountコンポーザブルを使用します。他のアニメーションに興味のある方は以下のサイトを参照してください

animationContentSizeのAnimationSpecパラメータを使用してアニメーションをカスタマイズすることが出来ます。
2024年7月8日現在提供されているのは下の6種類です。
1.spring -> ばねの力で動くアニメーション。dampingRatioでばねの弾性、stiffnessでばねの移動速度(剛性)を定義
2.tween -> 開始値と終了値の間を滑らかな曲線でアニメーション化
3.keyframes -> 特定のタイミングで特定の値にアニメーション化
4.repeatable -> 回数を指定してアニメーションを繰り返す
5.infiniteRepeatable -> 無限にアニメーションを繰り返す
6.snap -> 値を終了値にすぐに切り替える

AnimationSpecについてより詳しく知りたい方は以下のサイトを参照してください。

今回はスプリングアニメーションを例として使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var expanded by remember { mutableStateOf(false) }
    var expanded2 by remember { mutableStateOf(false) }
    var expanded3 by remember { mutableStateOf(false) }
    var expanded4 by remember { mutableStateOf(false) }

    Column(
        modifier = modifier
    ) {
        Card(
            modifier = Modifier
                .clickable { expanded = !expanded }
        ) {
            Text(
                text = "アニメーション"
            )
            if (expanded){
                Text(
                    modifier = Modifier.padding(top = 5.dp),
                    text = "なし",
                )
            }
        }

        Card(
            modifier = Modifier
                .clickable { expanded2 = !expanded2 }
                .animateContentSize(
                    animationSpec = spring(
                        dampingRatio = Spring.DampingRatioNoBouncy,
                        stiffness = Spring.StiffnessMedium
                    )
                )
        ) {
            Text(
                text = "アニメーションあり"
            )
            if (expanded2){
                Text(
                    modifier = Modifier.padding(top = 5.dp),
                    text = "弾力無し、収縮の速さは中",
                )
            }
        }

        Card(
            modifier = Modifier
                .clickable { expanded3 = !expanded3 }
                .animateContentSize(
                    animationSpec = spring(
                        dampingRatio = Spring.DampingRatioMediumBouncy,
                        stiffness = Spring.StiffnessMedium
                    )
                )
        ) {
            Text(
                text = "アニメーションあり"
            )
            if (expanded3){
                Text(
                    modifier = Modifier.padding(top = 5.dp),
                    text = "弾力アリ、収縮の速さは中",
                )
            }
        }

        Card(
            modifier = Modifier
                .clickable { expanded4 = !expanded4 }
                .animateContentSize(
                    animationSpec = spring(
                        dampingRatio = Spring.DampingRatioNoBouncy,
                        stiffness = Spring.StiffnessHigh
                    )
                )
        ) {
            Text(
                text = "アニメーションあり"
            )
            if (expanded4){
                Text(
                    modifier = Modifier.padding(top = 5.dp),
                    text = "弾力なし、収縮の速さは大",
                )
            }
        }
    }
}

Columnコンポーザブル

Columnコンポーザブルの公式レファレンスは以下のサイトです。

Columnの基本

Columnはレイアウト(要素を配置する際のルール)を垂直方向に指定します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 40.dp, start = 30.dp)
) {
    Column {
        Text(
            text = "要素1",
            fontSize = 30.sp,
            modifier = modifier
        )
        Text(
            text = "要素2",
            fontSize = 30.sp,
            modifier = modifier
        )
        Text(
            text = "要素3",
            fontSize = 30.sp,
            modifier = modifier
        )
    }
}

要素を水平方向で整える

要素を水平方向(x軸)で揃えたい場合は、horizontalAlignmentパラメータを使用します。
左詰めしたい場合はAlignment.Start
中央揃えしたい場合はAlignment.CenterHorizontally
右詰めしたい場合はAlignment.End

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Column(
            modifier = Modifier
                .height(200.dp)
                .width(300.dp)
                .background(Color.Gray),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            repeat(4){
                Text(
                    text = "要素${it}",
                )
            }
        }
    }
}

要素を垂直方向で整える

要素を垂直方向(Y軸)で整えたい場合は、verticalArrangementパラメータを使用します。
一番上に揃える -> Arrangement.Top
中央に揃える -> Arrangement.Center
下に揃える -> Arrangement.Bottom
一番上と下に要素を配置しその間に等間隔で配置する -> Arrangement.SpaceBetween
などがあります。他のものは下のドキュメントに記載されています。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Column(
            modifier = Modifier
                .height(200.dp)
                .width(300.dp)
                .background(Color.Gray),
            verticalArrangement = Arrangement.SpaceAround,
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            repeat(4){
                Text(
                    text = "要素${it}",
                )
            }
        }
    }
}

Rowコンポーザブル

Rowコンポーザブルの公式レファレンスは以下のサイトです。

Rowの基本

Columnはレイアウト(要素を配置する際のルール)を水平方向に指定します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Row(
            modifier = Modifier
                .height(100.dp)
                .width(300.dp)
                .background(Color.Gray),
        ) {
            repeat(4){
                Text(
                    text = "要素${it}",
                    modifier = modifier
                )
            }
        }
    }
}

要素を水平方向で整える

Columnの時と反対でこちらの場合はhorizontalArrangement引数を用います。
一番左に揃える -> Arrangement.Start
中央に揃える -> Arrangement.Center
右に揃える -> Arrangement.End
一番左と右に要素を配置しその間に等間隔で配置する -> Arrangement.SpaceBetween
などがあります。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Row(
            modifier = Modifier
                .height(200.dp)
                .width(400.dp)
                .background(Color.Gray),
            horizontalArrangement = Arrangement.Center,
        ) {
            repeat(4){
                Text(
                    text = "要素${it}",
                    modifier = modifier
                )
            }
        }
    }
}

要素を垂直方向で整える

要素を垂直方向で整える場合はverticalAlignment引数を用います。
上詰めしたい場合はAlignment.Top
中央揃えしたい場合はAlignment.CenterVertically
下詰めしたい場合はAlignment.Bottom

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Row(
            modifier = Modifier
                .height(200.dp)
                .width(400.dp)
                .background(Color.Gray),
            verticalAlignment = Alignment.CenterVertically
        ) {
            repeat(4){
                Text(
                    text = "要素${it}",
                    modifier = modifier
                )
            }
        }
    }
}

Boxコンポーザブル

Boxコンポーザブルの公式レファレンスは以下のサイトです。

Boxコンポーザブルの基本

Boxはレイアウト(要素を配置する際のルール)を指定しません。そのため、子のコンポーザブルの配置を手動で設定する必要があります。

要素の配置を整える

Boxコンポーザブルの要素は各自でパディングなどを用いる他に配置を整える便利な引数が存在します。それがcontentAlignmentです。
左上 -> Alignment.TopStart
中央上 -> Alignment.TopCenter
右上 -> Alignment.TopEnd
左中央 -> Alignment.CenterStart
…と計9つから選ぶことが出来ます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Box(
            modifier = Modifier
                .height(200.dp)
                .width(400.dp)
                .background(Color.Gray),
            contentAlignment = Alignment.CenterEnd
        ) {
            Text(
                text = "要素",
                modifier = modifier
            )
        }
    }
}

Cardコンポーザブル

Cardコンポーザブルの公式レファレンスは以下のサイトです。

CardコンポーザブルはColumn等と同様にコンテナとしての役割を果たします。ColumnやRowはレイアウトとしてより汎用的に用いられるのに対して、Cardは単一のコンテンツを表示することに焦点を当てています。具体的には、リストのアイテムを作成する時などによく用いられます。
また、マテリアルデザインに沿っているため、colorsパラメータを使用せずともコンポーザブルが最初から塗られているのも特徴です。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column(
        modifier = modifier
    ) {
        Card(
            modifier = Modifier
        ) {
            Text(
                text = "こんにちは"
            )
        }
    }
}

LazyColumnコンポーザブル、LazyRowコンポーザブル

LazyListに関する記事は以下のサイトを参照してください。

少し特殊なコンポーザブルとしてLazyColumn、LazyRowがあります。
これらは多数のアイテム又は長さが不明なリストを表示する必要がある場合に用います。
LazyColumnは縦方向のスクロールするリスト、LazyRowは横方向のスクロールするリストを作成します。
要素は1つの場合はitem()、リストなど要素が複数ある場合はitems()を使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    val listA = (1..20).toList()//1~20のリスト
    Box {
        LazyColumn(
            modifier = Modifier
                .height(200.dp)
                .width(400.dp)
                .background(Color.Gray),
        ) {
            item {
                Text(
                    text = "要素",
                    modifier = modifier
                )
            }
            items(5){
                Text(
                    text = "要素${it}",
                    modifier = modifier
                )
            }
            items(listA){
                Text(
                    text = "${it}",
                    modifier = modifier
                )
            }
        }
    }
}

LazyVerticalGridコンポーザブル、LazyHorizontalGridコンポーザブル

LazyGridに関する記事は以下のサイトを参照してください。

Gridとは3×3の様に複数の行と複数の列がある表示の仕方です。RowとColumnを足し合わせた感じです。
LazyVerticalGridは上下にスクロール可能で、LazyHorizontalGridは左右にスクロール可能です。
LazyColumnと同様にアイテムはitem又はitemsに入れます。

列数又は行数を操作する

LazyVerticalGridの場合はColumnパラメータ、LazyHorizontalGridの場合はRowパラメータを指定することが必須です。
ここではLazyVerticalGridの場合を紹介します。
各列の幅の最小値を指定する際はGridCells.Adaptiveを使用します。一方、列数を厳密に指定する場合はGridCells.Fixedを使用します。
個人的な使い分けはアイテムのサイズを一定以上にしたい場合はAdaptive、ボタンで列数を変えることが出来るようにしたいという場合はFixedが便利だと思います。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    val listA = (1..20).toList()//1~20のリスト
    Box {
        LazyVerticalGrid(
            columns = GridCells.Fixed(3), 
                    //GridCells.Adaptive(minSize = 60.dp),//400/60で6個の列ができることになる
            modifier = Modifier
                .height(200.dp)
                .width(400.dp)
                .background(Color.Gray),
        ) {
            item {
                Text(
                    text = "要素",
                    modifier = modifier
                )
            }
            items(5){
                Text(
                    text = "要素${it}",
                    modifier = modifier
                )
            }
            items(listA){
                Text(
                    text = "${it}",
                    modifier = modifier
                )
            }
        }
    }
}

Scaffoldコンポーザブル

Scaffoldコンポーザブルの公式レファレンスは以下のサイトです。

Scaffoldコンポーザブルはマテリアルデザインに則ったアプリを組み立てるために役立つAPIです。下のコードからわかるようにModifierの拡張関数などを用いて位置を指定していないのにも関わらずtop app barは一番上、bottom app barは一番下に来ています。つまり、Scaffoldコンポーザブルを使用すると、基本的な画面の構造を簡単に作れます。
※注意点としてinnerPaddingの値を使用する必要があります。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Scaffold(
        topBar = {
            Text(
                text = "Top app bar",
                fontSize = 30.sp
            )
        },
        bottomBar = {
            Text(
                text = "Bottom app bar",
                fontSize = 30.sp
            )
        },
        floatingActionButton = {
            FloatingActionButton(onClick = { /*TODO*/ }) {
                Icon(
                    imageVector = Icons.Filled.Add,
                    contentDescription = null
                )
            }
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier.padding(innerPadding)
        ) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Gray),
                contentAlignment = Alignment.Center
            ){
                Text(
                    text = "ここに中身",
                    color = Color.White,
                    fontSize = 30.sp
                )
            }
        }
    }
}

トップアプリバー

画面上部にあるその画面は何を表すものかを示すバーを作成したい場合はtopBarパラメータにトップアプリバーとして使用したいコンポーザブルを割り当てます。

ボトムアプリバー

bottomBarパラメータを使用するとアプリの下部に置きたいボトムアプリバーを簡単に定義できます。多くの場合はここにナビゲーションバー(ホーム画面などの移動ができるボタンの集まり)を配置します。

ボタン

新しく何かを追加するアクションを取れるようにする際、右下にボタンを追加したい場合があると思います。例えば、新規メールを作成する場合に押す+ボタン。この場合はfloatingActionButtonパラメータを使用します。

NavigationDrawer

NavigationDrawerに関する記事は以下のサイトです。

ナビゲーションバーを表示する方法の一つにナビゲーションドロワーがあります。これを使用すると、スワイプ等でナビゲーションバーを表示することが出来ます。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    ModalNavigationDrawer(
        drawerContent = {
            ModalDrawerSheet(
                modifier = Modifier.width(200.dp)
            ) {
                Text(text = "ナビゲーションドロワー")
                NavigationDrawerItem(
                    label = { Text(text = "ホーム画面") },
                    selected = false,
                    onClick = {  },
                    icon = {
                        Icon(
                            imageVector = Icons.Filled.Home,
                            contentDescription = null
                        )
                    }
                )
            }
        },
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Black)
        ){
            Text(
                text = "コンテンツ",
                fontSize = 60.sp,
                color = Color.White
            )
        }
    }
}

ナビゲーションドロワーの種類

ナビゲーションドロワーはModalNavigationDrawer, DismissibleNavigationDrawer, PermanentNavigationDrawerの3種類 があります。
ModalNavigationDrawerのは、横にスワイプしたらコンテンツの上に覆いかぶさるように表示されます。

DismissibleNavigationDrawerは、横にスワイプしたらコンテンツを左側に移動させて表示されます。
※この方法だとコンテンツはパディングする必要があるため注意

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    DismissibleNavigationDrawer(
        drawerContent = {
            DismissibleDrawerSheet(
                modifier = Modifier.width(200.dp)
            ) {
                Text(text = "ナビゲーションドロワー")
                NavigationDrawerItem(
                    label = { Text(text = "ホーム画面") },
                    selected = false,
                    onClick = {  },
                    icon = {
                        Icon(
                            imageVector = Icons.Filled.Home,
                            contentDescription = null
                        )
                    }
                )
            }
        },
    ) {
        Box(
            modifier = Modifier
                .padding(start = 160.dp)//コンテンツのパディング
                .fillMaxSize()
                .background(Color.Black)
        ){
            Text(
                text = "コンテンツ",
                fontSize = 60.sp,
                color = Color.White
            )
        }
    }
}

PermanentNavigationDrawerは、永続的に表示されます。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    PermanentNavigationDrawer(
        drawerContent = {
            PermanentDrawerSheet(
                modifier = Modifier.width(200.dp)
            ) {
                Text(text = "ナビゲーションドロワー")
                NavigationDrawerItem(
                    label = { Text(text = "ホーム画面") },
                    selected = false,
                    onClick = {  },
                    icon = {
                        Icon(
                            imageVector = Icons.Filled.Home,
                            contentDescription = null
                        )
                    }
                )
            }
        },
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Black)
        ){
            Text(
                text = "コンテンツ",
                fontSize = 60.sp,
                color = Color.White
            )
        }
    }
}

ナビゲーションドロワーの基本

ナビゲーションドロワーは、ModalNavigationDrawerならModalDrawerSheetのように、それぞれのドロワーシートを使用して要素を作ります。
そして、ドロワーシートの中に、ナビゲーションを表すボタンやラベルを入れます。この時に便利なのがNavigationDrawerItemコンポーザブルで、label, icon, onClickパラメータを用いることで簡単にアイコン、ラベル、クリック機能を実装できるようになります。

ボタンを押したらナビゲーションドロワーが出るようにする

左スワイプのほかにボタンを押したらナビゲーションドロワーが出るようにしたい場合はdrwerStateパラメータを使用します。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var drawerState by remember { mutableStateOf(DrawerState(initialValue = DrawerValue.Closed)) }

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ModalDrawerSheet(
                modifier = Modifier.width(200.dp)
            ) {
                Text(text = "ナビゲーションドロワー")
                NavigationDrawerItem(
                    label = { Text(text = "ホーム画面") },
                    selected = false,
                    onClick = {  },
                    icon = {
                        Icon(
                            imageVector = Icons.Filled.Home,
                            contentDescription = null
                        )
                    }
                )
            }
        },
    ) {
        Row(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Black)
        ){
            IconButton(
                modifier = Modifier.padding(start = 30.dp, top = 20.dp, end = 20.dp),
                onClick = { drawerState = DrawerState(initialValue = DrawerValue.Open) }
            ) {
                Icon(
                    imageVector = Icons.Filled.Menu,
                    contentDescription = null,
                    tint = Color.White,
                    modifier = Modifier
                        .height(60.dp)
                        .width(60.dp)
                )
            }
            Text(
                text = "コンテンツ",
                fontSize = 60.sp,
                color = Color.White
            )
        }
    }
}

要素となるコンポーザブル(Box, Text, Button, Image, TextField, Icon…)の共通点

ここでは、要素となるコンポーザブルでよく使用するModifierの拡張関数を紹介します。
Modifierの全ての拡張関数を知りたい方は以下のサイトを参照してください。

サイズを指定する

レイアウトに関するコンポーザブルと同様にModifierの拡張関数を使用します。
親となるコンポーザブルのサイズに合わせたい場合は、fillMaxHeight() 、fillMaxWidth()を使用します。両方使用する際はfillMaxSize()を使用します。
指定したサイズにしたい場合は、height()、width()を使用します。
サイズを指定しない場合はコンテンツの大きさになります。(下の例では文字列「こんにちは」が入る大きさになります。)

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .height(30.dp)
                    .width(120.dp)
                    .background(Color.Red)
            )
        }
    }
}

色を塗る

レイアウトに関するコンポーザブルと同様にModifierの拡張関数background()を使用します。(上のコードで使用)

スペースを取る

コンポーザブルの開始地点を親からスペースを取った地点から始める場合は、レイアウトに関するコンポーザブルと同様にModifierの拡張関数padding()を使用します。
左がstart、右がend、上がtop、下がendです。(上のコードで使用)

枠線を描く

枠線を書きたい場合も同様にborder()を使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .height(30.dp)
                    .width(120.dp)
                    .background(Color.Red)
                    .border(
                        border = BorderStroke(2.dp, Color.Yellow)
                    )
            )
        }
    }
}

親を基準にしてに配置する

親となるコンポーザブルのスタート、センター、エンド、トップ、ボトムに配置したい場合はModifierの拡張関数align()を用います。
※注意点として、レイアウトがColumnの場合はスタート、センター、エンドの3種類、Rowの場合はトップ、センター、ボトムの3種類、Boxの場合はそれらを掛け合わせた9種類を使用できます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box {
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .background(Color.Red)
                    .align(Alignment.End)
            )
        }
    }
}

位置を変更する

パン、ズーム、回転に使用されるマルチタッチ操作の検出にはtransformable拡張関数を使用します。位置を変更する際はパン操作を使用します。
そして、graphicsLayerのtranslationパラメータをを使用して検出した分だけ移動します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var offset by remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState{ _, offsetChange, _ ->
        offset += offsetChange
    }

    Box {
        Box(
            modifier = Modifier
                .height(200.dp)
                .width(300.dp)
                .background(Color.Gray)
                .clipToBounds()
        ) {
            Box(
                modifier = Modifier
                    .height(60.dp)
                    .width(60.dp)
                    .graphicsLayer(
                        translationX = offset.x,
                        translationY = offset.y
                    )
                    .transformable(state = state)
                    .border(
                        BorderStroke(6.dp, Color.Black)
                    )
                    .background(Color.Red),
            )
        }
    }
}

拡大/縮小する

パン、ズーム、回転に使用されるマルチタッチ操作の検出にはtransformable拡張関数を使用します。ズームを使用する際はズーム操作を使用します。
そして、graphicsLayerのscaleパラメータを使用して検出した分だけ拡大 / 縮小します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var scale by remember { mutableStateOf(1f) }
    val state = rememberTransformableState{ zoomChange, offsetChange, _ ->
        scale *= zoomChange
    }

    Box {
        Box(
            modifier = Modifier
                .height(400.dp)
                .width(300.dp)
                .background(Color.Gray)
                .clipToBounds(),
            contentAlignment = Alignment.Center
        ) {
            Box(
                modifier = Modifier
                    .height(80.dp)
                    .width(80.dp)
                    .graphicsLayer(
                        scaleX = scale,
                        scaleY = scale
                    )
                    .transformable(state = state)
                    .border(
                        BorderStroke(6.dp, Color.Black)
                    )
                    .background(Color.Red),
            )
        }
    }
}

回転する

パン、ズーム、回転に使用されるマルチタッチ操作の検出にはtransformable拡張関数を使用します。回転を使用する際は回転操作を使用します。
そして、graphicsLayerのrotationパラメータを使用して回転したい軸を選び回転します。
※この方法で回転させる場合は対象の二点をタッチして回す必要があります。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var rotation by remember { mutableStateOf(0f) }
    val state = rememberTransformableState{ _, _, rotationChange ->
        rotation += rotationChange
    }

    Box {
        Box(
            modifier = Modifier
                .height(400.dp)
                .width(300.dp)
                .background(Color.Gray)
                .clipToBounds(),
            contentAlignment = Alignment.Center
        ) {
            Box(
                modifier = Modifier
                    .height(80.dp)
                    .width(80.dp)
                    .graphicsLayer(
                        rotationZ = rotation
                    )
                    .transformable(state = state)
                    .border(
                        BorderStroke(6.dp, Color.Black)
                    )
                    .background(Color.Red),
            )
        }
    }
}

Textコンポーザブル

Textコンポーザブルの公式レファレンスは以下のサイトです。

Textコンポーザブルの基本

Textコンポーザブルはその名の通り文字列を描画するためのコンポーザブルです。textパラメータの中に表記したい文字列を書きます。" "の中に文字(数字から漢字まで対応)を書くか、String型の変数を割り当てる、Int型の数値などを描画したい場合は"${ }"を使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    val greeting: String = "こんばんは"
    val number: Int = 1

    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .background(Color.White)
            )
            Text(
                text = greeting,
                modifier = modifier
                    .background(Color.White)
            )
            Text(
                text = "${number}",
                modifier = modifier
                    .background(Color.White)
            )
        }
    }
}

文字の色を指定する

Textコンポーザブルで、文字の色を指定する場合はcolorパラメータに使用したい色を割り当てます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .background(Color.White),
                color = Color.Red
            )
        }
    }
}

文字の大きさを指定する

文字の大きさを指定したい場合はfontSizeパラメータに希望するふぁおんとサイズを割り当てます。この時.spを使用することに気を付けてください。
※物によって大きさを指定することは一貫性が取れない、変更が困難になることがあります。後述するMaterialThemeの使用をお勧めします。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .background(Color.White),
                fontSize = 30.sp
            )
        }
    }
}

文字の太さを指定する

文字の太さを指定するにはstrokeWeightパラメータに値を割り当てます。
太字にしたい場合はFontWeight.Boldを使用します。薄くしたいならLight、標準ならMediumを使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .background(Color.White),
                fontWeight = FontWeight.Bold
            )
        }
    }
}

文字列の配置を整える

収容する文字列に対してTextコンポーザブルが大きい場合などに文字列の位置を中央揃えにしたい場合などがあると思います。
その場合はtextAlignパラメータを使用します。
左揃えならTextAlign.Start、中央揃えならTextAlign.Center、右揃えならTextAlign.Endを使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは",
                modifier = modifier
                    .height(60.dp)
                    .width(120.dp)
                    .background(Color.White),
                textAlign = TextAlign.Start
            )
        }
    }
}

テキストの行数を指定する

テキストが親コンポーザブルより長くなる、指定した幅を超えるという場合にテキストが自動で複数行になることがあります。
その際に、テキストの行の最大値をmaxLineパラメータを使用することで指定することが出来ます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは!今日もいい天気ですね!",
                modifier = modifier
                    .width(120.dp)
                    .background(Color.White),
                maxLines = 1
            )
        }
    }
}

テキストがコンポーザブルからはみ出した際の挙動を定義する

テキストが長すぎてTextコンポーザブルから溢れる(オーバーフローする)ことがあります。
その場合の挙動を定義するためにtextOverflowパラメータを使用します。
溢れたテキストを切り取りたい場合はTextOverflow.Clip、テキストがオーバーフローしたことを示す場合はTextOverflow.Ellipsisを使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "こんにちは!今日もいい天気ですね!",
                modifier = modifier
                    .width(120.dp)
                    .background(Color.White),
                maxLines = 1,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}

Buttonコンポーザブル

Buttonコンポーザブルの公式レファレンスは以下のサイトです。

Buttonコンポーザブルの基本

Buttonコンポーザブルはその名の通りクリックできるボタンを描画するために用いられます。
onClickパラメータにボタンを押した際の挙動を表示します。
また、Buttonコンポーザブルは他のコンポーザブルを要素として取ることが出来ます。勿論、複数のコンポーザブルを子に持つことが出来ます。また、レイアウトはデフォルトでRowです。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Button(
                onClick = {  }
            ) {
                Text(text = "ボタン")
            }
        }
    }
}

ボタンをクリックしても反応しないようにする

何かしらの条件を満たすとボタンを押せるようになるようにしたい場合などで有効なのがenableパラメータです。このパラメータがfalseの場合はボタンが反応することがありません。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    val state = false
    
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Button(
                onClick = {  },
                enabled = state
            ) {
                Text(text = "ボタン")
            }
        }
    }
}

ボタンの枠線の色、太さを調整する

ボタンの枠線の色、太さを調整するためにはborderパラメータに値を割り当てます。ここでの注意点は、Modifierの拡張関数borderと違って、このborderパラメータではボタンの枠線を綺麗に塗ることが出来ます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Button(
                modifier = modifier.border(BorderStroke(2.dp, Color.White)),
                onClick = {  },
                border = BorderStroke(2.dp, Color.Yellow)
            ) {
                Text(text = "ボタン")
            }
        }
    }
}

ボタンの形を変更する

ボタンの形を変更するにはshapeパラメータを使用します。
このパラメータを使用しない場合は楕円のボタンとなります。
もし、四角形のボタンを作成したい場合はshapeパラメータにRoundedCornerShape(0.dp)を割り当てます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(200.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Button(
                modifier = modifier,
                onClick = {  },
                shape = RoundedCornerShape(0.dp)
            ) {
                Text(text = "ボタン")
            }
        }
    }
}

ボタンの種類

ボタンにはButtonコンポーザブルのほかにTextButton、ElevatedButton、OutlinedButton、FilledTonalButtonなどがあります。
ボタンのデザインに応じて適切に選んでください。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Column(
            modifier = modifier
                .height(400.dp)
                .width(200.dp)
                .background(Color.Gray)
        ) {
            Button(
                modifier = modifier,
                onClick = {  },
            ) {
                Text(text = "塗りつぶしボタン")
            }
            FilledTonalButton(
                modifier = modifier,
                onClick = {  },
            ) {
                Text(text = "塗りつぶしの色調ボタン")
            }
            OutlinedButton(
                modifier = modifier,
                onClick = {  },
            ) {
                Text(text = "枠線付きのボタン")
            }
            ElevatedButton(
                modifier = modifier,
                onClick = {  },
            ) {
                Text(text = "立体ボタン")
            }
            TextButton(
                modifier = modifier,
                onClick = {  },
            ) {
                Text(text = "テキストボタン")
            }
        }
    }
}

Imageコンポーザブル

Imageコンポーザブルの公式レファレンスは以下のサイトです。

Imageコンポーザブルは画面に画像を描画する時に用います。
※ここでの注意点は既にアプリ側で保存している画像の表示に用いるということです。インターネットから読み込んだ画像の表示、端末内に保存されている画像の表示はCoilというサードパーティライブラリの中のAsyncImageという別のコンポーザブルを使用します。

プロジェクトに画像を追加する

ここでは、プロジェクトに使用する画像を追加する方法を説明します。
前提として画像のファイルはPNG形式であることを確認してください。また、名前は全てアルファベットでかつ小文字のみであることを確認してください。
[View] -> [Tool Window] -> [Resource Manager] -> +ボタン -> [Import Drawables] -> 目的の画像をクリック -> [OK] -> [Next] -> [Import]

画像を追加できた場合は[app] -> [res] -> [drawable]で確認できます。

Imageコンポーザブルの基本

Imageコンポーザブルは基本的に2つのパラメータが必須となります。一つは画像のリソースを表記するpainter、もう一つは画像が何を表すかを表記するcontentDescription。contentDescriptionの方は目が見えない方が使用した場合に読み上げたりします。必要ないと感じた場合はnullで大丈夫です。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Image(
            modifier = modifier,
            painter = painterResource(id = R.mipmap.image_example),//この部分は各自の画像のリソースを書いてください
            contentDescription = "お試し画像"
        )
    }
}

Imageコンポーザブルでエラーが出る場合

Imageコンポーザブルを使用する場合、以下のように画像のサイズが大きすぎるというエラーが起きることがあります。
話によれば数100キロバイトをこえると起きるらしい。(スマホのカメラで撮った画像でも2MBぐらいはある)
【エラー文】
java.lang.RuntimeException: Canvas: trying to draw too large(146446976bytes) bitmap.

この場合の対処法の一つにdrawable-xxhdpiフォルダーを使用するという方法があります。
プロジェクトビューから目的の画像をmidmap-xxhdpiにドラッグアンドドロップします。その後、[Refactor]を押せば完了です。
※この際、リソースがR.drawable.~からR.midmap.~に変わることに気を付けてください

画像のサイズをImageコンポーザブルに合わせる

Modifierの拡張関数を用いてImageコンポーザブルのサイズを確定した場合、そのImageコンポーザブルの大きさと画像の大きさが合わない場合があります。この場合において画像をどうするかを決めたい場合はcontentScaleパラメータを使用します。
どれも一長一短ですので用途に応じて適したものを使用してください。

Imageコンポーザブルの大きさと同等にしてかつ、画像の高さと幅を等倍(画像の違和感なし)したい場合 -> ContensScale.Crop
※ただし、納まりきらなかった部分はカットされる。

Imageコンポーザブルの大きさと同等にしてかつ、画像をカットしないように高さと幅をそれぞれ拡大/縮小する場合-> ContensScale.FillBounds
※この方法だと拡大と縮小の倍率が高さと幅で異なるため画像に違和感が生じる可能性があります。

Imageコンポーザブルの高さのみ同等にしてかつ、画像を等倍で拡大/縮小する場合-> ContensScale.FillHeight
Imageコンポーザブルの幅のみ同等にしてかつ、画像を等倍で拡大/縮小する場合-> ContensScale.FillWidth

画像の高さと幅を、Imageコンポーザブルの高さと幅と同等かそれ以下にしてかつ、画像を等倍で拡大/縮小する場合 -> ContensScale.Fit
つまりは、画像をカットすることなく、高さ又は幅がImageコンポーザブルと同等になるように画像を縮小/拡大する。
contentScaleパラメータに何も書いていない場合はFitになっている。

Imageコンポーザブル内の画像の配置を変える

alignmentパラメータを用いることで、Boxコンポーザブル内の要素同様に、3 × 3 = 9個の配置を自由に選ぶことが出来ます。
デフォルトでは中央揃えになっています。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Image(
            modifier = modifier
                .height(400.dp)
                .width(400.dp)
                .background(Color.Gray),
            painter = painterResource(id = R.mipmap.image_example_2),//この部分は各自の画像のリソースを書いてください
            contentDescription = "お試し画像",
            contentScale = ContentScale.Fit,
            alignment = Alignment.TopStart
        )
    }
}

画像を透過させる

画像を透過させたい場合はalphaパラメータを変更します。この値はFloat型であり、0に近づくにつれ画像が薄くなっていきます。0で透明になります。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        Image(
            modifier = modifier
                .height(400.dp)
                .width(400.dp)
                .background(Color.Gray),
            painter = painterResource(id = R.mipmap.image_example_2),//この部分は各自の画像のリソースを書いてください
            contentDescription = "お試し画像",
            contentScale = ContentScale.Fit,
            alpha = 0.5f
        )
    }
}

TextFieldコンポーザブル

TextFieldコンポーザブルの基本

TextFieldコンポーザブルは、ユーザーにキーボードを用いて何かを入力してもらう場合に使用します。具体例として検索ワードがあります。
TextFeildは2つのパラメータが必須となります。
1つ目がvalueで、この値がTextFieldの中身になります。
2つ目がonValueChangeで、ここでvalueが変化した(≒キーボードで入力した)際の挙動を記述します
※注意点として、TextFieldはマテリアルデザインの実装であるため、今後変更される可能性があります。その為、コンポーザブルの上に@OptIn(ExperimentalMaterial3Api::class)というアノテーションを付けて承認する必要があります。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Box{
        TextField(
            value = "",
            onValueChange = {},
            modifier = modifier.width(100.dp)
        )
    }
}

TextFieldの値を保持する

上のコードの例ではキーボードで入力してもテキストフィールドに値が入らないため入力が表示されません。そこで以下のようにvalueとonValueChangeを書くことにより値を保持できます。
ここで変数inputがただのvarで定義しますと値が変更された際に再コンポーズ(≒UIが作り直されて新しい値が表示される)が起きません。
そこで、remember関数とmutableStateOf関数を用いることで状態の保存と更新を適切に行います。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier.width(100.dp)
        )
    }
}

TextFieldの値を保持する(ViewModel編)

アプリ開発について学んだことがある人はUIについて記述する場所(UI要素)と、データの保持やアプリロジックなどを行う場所(ViewModelなどの状態ホルダー)で分けることが良いアプリを作るには必須であることをご存じだと思います。
その考えのもと1つ上のコードを見ると、データ(input)の保持とアプリロジック(onValueChangeのラムダ)がUI要素に含まれているため不適切と言えます。
そこで、ViewModelを使ってTextFieldを表示します。
今回はMutableStateを使用しています。StateFlowなどのリアクティブストリームの使用は非同期の遅延があるため公式からも推奨されていません。

//build.gradle.kts (Module :app)に以下の依存関係の追加
dependencies {
// ...
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
}
import androidx.lifecycle.viewmodel.compose.viewModel
//...

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp),
    viewModel: ExampleViewModel = viewModel()
) {

    Box{
        TextField(
            value = viewModel.input,
            onValueChange = { onValueChange ->
                viewModel.changeTextField(onValueChange)
            },
            modifier = modifier.width(100.dp)
        )
    }
}

//viewModel
class ExampleViewModel: ViewModel() {
    var input by mutableStateOf("")
        private set

    fun changeTextField(onValueChange: String){
        input = onValueChange
    }
}

テキストフィールドにラベルを付ける

テキストフィールドが何の入力を求めているかを一目で伝える手段としてラベルがあります。labelパラメータを使用することでラベルを付けることが出来ます。このラムダの中にはコンポーザブルを記述します。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier.width(100.dp),
            label = { Text(text = "氏名")},
        )
    }
}

テキストフィールドに記入例を記載する

アンケートの紙などを見ると記入例に「山田 太郎」のような名前が書かれていることがあります。そのように記入例を書きたい場合はplaceholderパラメータを使用します。
この記入例は値が入っていない場合のみ見えます。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            placeholder = { Text(text = "アンド ロイど") }
        )
    }
}

TextFieldに入力できないようにする

テキストフィールドに入力できないようにしたい場合はenableパラメータを使用します。このパラメータがfalseの場合はテキストフィールドを押したとしても反応がありません。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            enabled = false
        )
    }
}

TextFieldの値を変更できないようにして、値をコピーできるようにしたい

readOnlyパラメータをtrueにすると、TextFieldを変更することはできませんが、その中身の値を長押しで選択しコピーすることが出来ます。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("こんにちは") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            readOnly = true
        )
    }
}

スクロール可能な1行のテキストフィールドにする

singleLineパラメータをtrueにすると、横スクロール可能な1行のテキストフィールドになります。
よく見るテキストフィールドは大体singleLineパラメータがtrueだと思います。
このパラメータをtrueにすると自動的にmaxLines = 1となることに気を付けてください。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            singleLine = true,
        )
    }
}

テキストフィールドの表示する行数の調整

テキストフィールドの表示する行の最大値を作成したい場合はmaxLinesパラメータに行数を指定します。逆に最小値はminLinesで指定します。
この行からはみ出た文字は縦スクロールすることで見ることが出来ます。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            maxLines = 2
        )
    }
}

キーボードをカスタムする

ユーザーが入力する際に画面にキーボードが出てきます。
ここで、購入個数を入力したいのに数字のみのキーボードが出てくると、わざわざキーボードの切り替えをする手間がなくなり、ユーザーエクスペリエンスが向上します。
このように出現させたいキーボードのタイプを変更したい場合は、keybordOptionsパラメータを使用します。
数字のみのキーボードを表示したい場合 -> KeyboardOptions( keyboardType = KeyboardType.Number )

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number
            )
        )
    }
}

通常のキーボードを表示したい場合 -> KeyboardOptions( keyboardType = KeyboardType.Text )

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }

    Box{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Text
            )
        )
    }
}

キーボードのアクションボタンを変更する

キーボードのアクションボタンとは右下の青いボタンです。
このボタンをカスタムすることで次のテキストフィールドにボタンを押すだけでとぶこともできるようになります。

次のテキストフィールドに行きたい場合 -> keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next )
前のテキストフィールドに戻りたい場合 -> keyboardOptions = KeyboardOptions( imeAction = ImeAction.Previous )
入力を完了しキーボードを取り下げたい場合 -> keyboardOptions = KeyboardOptions( imeAction = ImeAction.Done )

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }
    var input2 by remember { mutableStateOf("") }

    Column{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Next
            )
        )
        TextField(
            value = input2,
            onValueChange = { onValueChange ->
                input2 = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Text,
                imeAction = ImeAction.Done
            )
        )
    }
}

テキストフィールドの種類

テキストフィールドの見た目はTextField, OutLinedTextField, BasicTextFieldの主に3種類があります。用途によって選択してください。
※BasicTextFieldの場合label, placeholderパラメータがありません。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    var input by remember { mutableStateOf("") }
    var input2 by remember { mutableStateOf("") }
    var input3 by remember { mutableStateOf("BasicTextField" ) }

    Column{
        TextField(
            value = input,
            onValueChange = { onValueChange ->
                input = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            label = { Text(text = "TextField")},
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Next
            )
        )
        OutlinedTextField(
            value = input2,
            onValueChange = { onValueChange ->
                input2 = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            label = { Text(text = "OutlinedTextField")},
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Text,
                imeAction = ImeAction.Next
            )
        )
        BasicTextField(
            value = input3,
            onValueChange = { onValueChange ->
                input3 = onValueChange
            },
            modifier = modifier
                .width(200.dp),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Password,
                imeAction = ImeAction.Previous
            )
        )
    }
}

Iconコンポーザブル

Iconコンポーザブルの公式レファレンスは以下のサイトです。

Iconコンポーザブルはその名の通りアイコンを描画する際に使用するコンポーザブルです。
ここでは、アイコンを描画する方法として以下の3種類を提示します。
1.備え付けのマテリアルアイコンを使用する方法
2.アイコンをインポートして使用する方法
3.全てのマテリアルアイコンを使用する方法

Iconの基本

Iconは基本的に2つのパラメータをとります。
1つ目はimageVector または、painter。
2つ目はアイコンが何を表しているかを書くcontentDescription。必要ない場合はnullでも構いません。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Icon(
            imageVector = Icons.Outlined.KeyboardArrowUp,
            contentDescription = null,
            modifier = modifier
                .height(60.dp)
                .width(60.dp)
        )
    }
}

1.備え付けのマテリアルアイコンを使用する方法

この方法のメリットは事前定義されているものを用いるため依存関係をわざわざ書く必要が無く簡単に使用できます。
また、大きさ(Modifierの拡張関数を使用)、テーマ、色も変更可能です。ただし、使用できるアイコンの種類は約50個と少ないです。
この方法ではimageVectorパラメータにIcons.(好きな塗り方).(アイコンの形)を書きます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Icon(
            imageVector = Icons.Outlined.KeyboardArrowUp,
            contentDescription = null,
            modifier = modifier
                .height(60.dp)
                .width(60.dp)
        )
    }
}

アイコンのテーマ(塗り方や形)を指定する

Icons.の後にテーマを指定します。
テーマはFilled, Outlined, Rounded, Sharp, Twotoneの5種類(Defaultはその中のいずれか)があります。
Filled -> 塗りつぶし
Outlined -> 外側のみ塗る
Rounded -> 角に丸みを帯びさせる
sharp -> 角を鋭利にする
Twotone -> 2色使って塗る
アプリに応じて使用したいテーマを設定してください。また、一貫性を保つためにアプリを通じてテーマを一つにすることが公式から推奨されています。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        IconAndDescription(
            imageVector = Icons.Default.ShoppingCart,
            description = "Default"
        )
        IconAndDescription(
            imageVector = Icons.Filled.ShoppingCart,
            description = "Filled"
        )
        IconAndDescription(
            imageVector = Icons.Outlined.ShoppingCart,
            description = "Outlined"
        )
        IconAndDescription(
            imageVector = Icons.Rounded.ShoppingCart,
            description = "Rounded"
        )
        IconAndDescription(
            imageVector = Icons.Sharp.ShoppingCart,
            description = "Sharp"
        )
        IconAndDescription(
            imageVector = Icons.TwoTone.ShoppingCart,
            description = "TwoTone"
        )
    }
}

@Composable
fun IconAndDescription(
    modifier: Modifier = Modifier
        .padding(start = 20.dp),
    imageVector: ImageVector,
    description: String
){
    Column {
        Text(
            text = description,
            modifier = modifier
                .padding(top = 10.dp)
        )
        Icon(
            imageVector = imageVector,
            contentDescription = null,
            modifier = modifier
                .padding(top = 5.dp)
                .height(40.dp)
                .width(40.dp)
        )
        Divider(
            modifier = Modifier
                .width(100.dp),
            thickness = 3.dp
        )
    }
}

アイコンの色を変更する

アイコンの色を変更したい場合はtintパラメータを使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Icon(
            imageVector = Icons.Filled.ShoppingCart,
            contentDescription = null,
            modifier = modifier
                .height(40.dp)
                .width(40.dp),
            tint = Color.Red
        )
    }
}

2.アイコンをインポートして使用する方法

この方法でIconコンポーザブルを使用する場合に必要となるパラメータはリソースを示すpainterです。
この方法でも大きさ、色はModifierの拡張関数、tintパラメータを使用して後から変更できます。

アイコンのインポート

ここではアイコンのインポート方法の一例を示します。
[View] -> [Tool Window] -> [Resource Manager] -> +ボタン -> [Vector Asset]
Clip artでテーマとアイコンの形を指定、サイズで大きさを指定、Colorで色を指定します。その後、[Next] -> [Finish]で完了です。

Iconでインポートしたベクターアセットの使用

painterResource()を使用してリソースID(先ほど保存した場所)からPainterを作成し、それをpainterパラメータに割り当てます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Icon(
            painter = painterResource(id = R.drawable.twotone_commute_40),//ここには自分が保存した場所を記入して下さい
            contentDescription = null,
            modifier = modifier
//                .height(160.dp)
//                .width(160.dp),
//            tint = Color.Red
        )
    }
}

3.全てのマテリアルアイコンを使用する方法(個人的におすすめ)

下のサイトで載っている多種多様なマテリアルアイコンを使用する場合はGradle依存関係を追加する必要があります。

//build.gradle.kts (Module: app)ファイル内
implementation("androidx.compose.material:material-icons-extended")

この依存関係以外は1番目の方法と同様に色もスタイルも大きさも自由に変更できます。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Icon(
            imageVector = Icons.Filled.Bolt,
            contentDescription = null,
            modifier = modifier
                .height(160.dp)
                .width(160.dp),
            tint = Color.Red
        )
    }
}

IconButtonコンポーザブル

IconBttonコンポーザブルの公式レファレンスは以下のサイトです。

IconButtonはアイコンをクリックできるようにする場合に使用するコンポーザブルです。個人的にもアイコンを使用する際はIconButtonコンポーザブルを併用することが多いと思います。
IconBttonの中のcontent(≒アイコン)は中央に配置されます。また、アイコンボタンはアイコンをクリックするという観点から、最低でも48dp x 48dpであることが推奨されています。
Buttonコンポーザブル同様にクリックした際の動作を記述するonClickパラメータは必須であり、ボタンを有効にするかどうかのenableパラメータなどもあります。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        IconButton(
            onClick = {  },
            modifier = modifier
                .height(60.dp)
                .width(60.dp)
                .background(Color.Gray)
        ) {
            Icon(
                imageVector = Icons.Filled.ShoppingCart,
                contentDescription = null,
                modifier = Modifier
                    .height(30.dp)
                    .width(30.dp),
                tint = Color.Red
            )
        }
    }
}

Dividerコンポーザブル

Dividerコンポーザブルの公式レファレンスは以下のサイトです。

Dividerコンポーザブルの基本

Dividerコンポーザブルは分割線を描画するために使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Text(text = "アンドロイド")
        Divider()
        Text(text = "あぷり")
    }
}

分割線の長さを調節する

Dividerの長さを調節したい場合は、Modifierの拡張関数を使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Text(
            modifier = modifier,
            text = "アンドロイド"
        )
        Divider(
            modifier = modifier
                .width(40.dp)
        )
        Text(
            modifier = modifier,
            text = "あぷり"
        )
    }
}

分割線の太さを変更する

分割線の太さを変更したい場合はthicknessパラメータを使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Text(
            modifier = modifier,
            text = "アンドロイド"
        )
        Divider(
            modifier = modifier
                .width(40.dp),
            thickness = 8.dp
        )
        Text(
            modifier = modifier,
            text = "あぷり"
        )
    }
}

分割線の色を指定する

分割線の色を指定したい場合はcolorパラメータを使用します。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Text(
            modifier = modifier,
            text = "アンドロイド"
        )
        Divider(
            modifier = modifier
                .width(40.dp),
            thickness = 8.dp,
            color = Color.Yellow
        )
        Text(
            modifier = modifier,
            text = "あぷり"
        )
    }
}

垂直方向の分割線を描画する

垂直方向の分割線を作成する場合の方法の一つに、Modifierの拡張関数を使用し、widthに線の太さ、heightに線の長さを入れる方法があります。
※注意点としてはこの方法で太さを指定した場合はthicknessパラメータに何を入れても無効となります。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Text(
            modifier = modifier,
            text = "アンドロイド"
        )
        Divider(
            modifier = modifier
                .width(1.dp)
                .height(20.dp)
        )
        Text(
            modifier = modifier,
            text = "あぷり"
        )
    }
}

HorizontalDivider(水平分割線), VerticalDivider(垂直分割線)コンポーザブル

HorizontalDivider, VerticalDividerコンポーザブルの公式レファレンスは以下のサイトです。

Material3では、Dividerをより使いやすくしたHorizontalDivider, VerticalDividerが存在します。また、Dividerは徐々に廃止されることとなるため、DividerからHorizontalDivider, VerticalDividerに置き換える必要があります。
これらを使用する際はGradle依存関係を追加する必要があります。

//build.gradle.kts (Module: app)ファイル内
implementation("androidx.compose.material3:material3:1.3.0-beta03")
@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column{
        Text(
            modifier = modifier,
            text = "アンドロイド"
        )
        HorizontalDivider(
            modifier = modifier
                .width(30.dp),
            thickness = 8.dp,
            color = Color.Red
        )
        VerticalDivider(
            modifier = modifier
                .height(30.dp),
            thickness = 8.dp,
            color = Color.Red
        )
        Text(
            modifier = modifier,
            text = "あぷり"
        )
    }
}

UIに関する内容(Material Theme)

このマテリアルテーマに関する手法で参照にした記事は以下の記事です。

ボタンの色やフォントは特別な設定(colorパラメータの操作)をしない限りデフォルトのフォントと色が使用されます。
この色とフォントを一括で変える場合に使用するのがマテリアルテーマビルダーです。

色のパターンの設定

最初のページでアプリのイメージに合う色をPrimary, Secondary, …を変更して決めます。この時に横の画面では実際にボタンなどがどのような色になるか表示されているためそこも参考にしてください。

色が決まりましたら、右下の[pick your font]を選択し、今回は色の追加のみですのでタイポグラフィを選択せずに[Export theme]を選び、Jetpack Compose(Theme.kt)を[Export]します。これでダウンロードは完了です。

次に、ダウンロードしたColor.ktファイルを開いてコピーし、プロジェクトのColor.ktファイルに貼り付けます。
※ダウンロードしたColor.ktファイルはVSCodeでも開けます。

同様にTheme.ktファイルもコピー&ペーストします。
この時、コンポーザブル名(ほとんどの場合はMainActivityに書いてある)と、タイポグラフィの名前(Type.ktファイルに書いてある)は個人で設定したものを適用しないといけないので書き換えてください。

タイポグラフィ(=フォントなどを含む文字書式)の追加

下のサイトから好きなフォントを探します。
この時、FilterのLanguageをJapaneseにすることをお勧めします

タイトル用とボディ用のフォントそれぞれお好みのものを1種類ずつ選んだら、[Get Font]を押し、最後に[Download all]を押します。

ダウンロードしたttfファイルをAndroidのプロジェクトのフォントディレクトリにドラッグします。フォントディレクトリがない場合[res]を右クリック- > [New] -> [Directory]で作成します。
ドラッグするとファイルに大文字と"-"が含まれているためエラーが出ます。そのため、右クリック -> [Refactor] -> [Rename]で小文字のみに変更します。

プロジェクトにフォントを追加したら、次はType.ktを以下のように変更し、ボディ用のフォントとタイトル用のフォントをそれぞれ定義します。

val bodyFontFamily = FontFamily(
    Font(R.font.dot_gothic16_regular)
)

val displayFontFamily = FontFamily(
    Font(R.font.dela_gothic_one_regular)
)

val baseline = Typography()

val Typography = Typography(
    displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily,),
    displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily),
    displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily),
    headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily),
    headlineMedium = baseline.headlineMedium.copy(fontFamily = displayFontFamily),
    headlineSmall = baseline.headlineSmall.copy(fontFamily = displayFontFamily),
    titleLarge = baseline.titleLarge.copy(fontFamily = displayFontFamily,fontSize = 40.sp),
    titleMedium = baseline.titleMedium.copy(fontFamily = displayFontFamily),
    titleSmall = baseline.titleSmall.copy(fontFamily = displayFontFamily),
    bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily),
    bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily),
    bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily),
    labelLarge = baseline.labelLarge.copy(fontFamily = bodyFontFamily),
    labelMedium = baseline.labelMedium.copy(fontFamily = bodyFontFamily),
    labelSmall = baseline.labelSmall.copy(fontFamily = bodyFontFamily),
)

また、フォントの大きさを変更したい場合はfontSizeを変更します。

bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily, fontSize = 30.sp)

その後、Textコンポーザブルのstyleを変更することで今回定義したタイポグラフィを使用することが出来る様になります。

@Composable
fun Example(
    modifier: Modifier = Modifier
        .padding(top = 20.dp, start = 30.dp)
) {
    Column(
        modifier = modifier
    ) {
        Text(
            modifier = Modifier.padding(top = 20.dp),
            text = "デフォルト"
        )
        Text(
            modifier = Modifier.padding(top = 20.dp),
            text = "titleLarge",
            style = MaterialTheme.typography.titleLarge
        )
        Text(
            modifier = Modifier.padding(top = 20.dp),
            text = "bodyLarge",
            style = MaterialTheme.typography.bodyLarge
        )
        Text(
            modifier = Modifier.padding(top = 20.dp),
            text = "bodySmall",
            style = MaterialTheme.typography.bodySmall
        )
    }
}

さいごに

今回は非常によく使用するコンポーザブルのみ紹介しました。今回紹介しきれていないコンポーザブルもたくさんありますので是非調べてみてください。chatGPTなどを使用して用途に合ったコンポーザブル名を聞き出すのも面白いと思いますよ。
次回はRoomライブラリなどのよく使用するサードパーティライブラリについてまとめた記事を出す予定です。内容量が多いため投稿日は未定ですが楽しみにお待ちください!

この記事が気に入ったらサポートをしてみませんか?