Kotlin初心者がJetpack Composeを5日間勉強してみたら

# はじめに
皆さん初めまして:blush:
今回が初めての投稿になりますので生暖かい目で見守っていただけたら嬉しいです。

本記事では、Kotlin初心者がJetpack Composeを5日間勉強してみたらどの程度できるようになるのか。また、これからJetpack Composeを勉強する方に向けて僕が勉強したロードマップを提供できたらなと思い記事にしました。

本記事は5日間のうちの一日目の記事です。

### 注意
＊ 本記事はすでに5日経ってから書いています。
本記事では参考にさせていただいた動画などを紹介するのですが、学んだ内容を大まかに紹介するに当たり、著作権のためコードは全てオリジナルにしています。

# 筆者のレベル感
Flutterのインターンを一年ほど経験していて、プログラミング初心者ではありません。
JetpackComposeやFlutterは宣言的UIということでUI部分に関しては共通する部分が多く理解しやすかったです。

# 目次
- [はじめに](#はじめに)
- [結果](#結果)
- [やったこと](#やったこと)
- [作ってみた](#作ってみた)
- [便利なボタン作ってみた](#便利なボタン作ってみた)
- [まとめ](#まとめ)

# 結果
- 簡単なUIの構築の仕方
- ローカル変数の使い方
- ViewModelを使用した簡単な状態管理とロジック実装の仕方
- 共通コンポーネントの作成の仕方
- sealedClassを使用したコンポーネントの作成の仕方

ができるようになりました:raised_hands:
1日目は約5時間勉強しました:alarm_clock:
これから具体的にやった事とできるようになった事を紹介していきます。

# やったこと
最初は何から手をつければいいか分からなくなりますよね。僕もわかりませんでした。
良い動画教材はないかなとUdemyをぽちぽちしていたところ、めっちゃ良さげなものがあるじゃないか！ということで購入し、実際にやってみました。

https://www.udemy.com/share/107nle3@GQLZMSiaQUf4WFgrjfVTo27lB_WQltNvROKitSGufFzP3f8ZfkW4ZIshNd7YKojTyQ==/

この教材、めちゃめちゃ良いです:v:
ぶっちゃけ5日間した事はこの教材で学んで、それを深掘りし自分なりにコードを書いてみたり調べてみたりしただけです...:frowning2:
しかし、それだけでもClean Architectureに基づいたアプリが作れちゃうんです！！
皆さんも是非やってみてください〜:ok_hand:


## 作ってみた
![kotlin一日目動画.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3826022/88d84472-a8e7-1a81-1802-75dd7ed14e07.gif)

ちょっとわかりにくいですが 氏名と名前を入力し、ボタンを押すとフルネームが表示されるというシンプルなものです。ここで大事なのはViewModelを使ったという事ですね。簡単にですがViewModelを使用した状態管理が理解できました！

JetpackComposeはViewModelを使用することを公式に推奨していてviewModelを継承することで色々良い感じにやってくれるんですね〜（他の言語だとなんちゃってViewModelを自分で作ってそれっぽい事をしているイメージ）

https://developer.android.com/codelabs/basic-android-kotlin-compose-viewmodel-and-state?hl=ja#0

ViewModelとUIの連携はだいたいこんな感じでかく。
stateの変更はViewModelだけで行ったりした方が良いんだろうけどとりあえず...

```Kotlin.MainActivity.kt
class MainActivity : ComponentActivity() {
    private val viewModel by viewModels<TestViewModel>() //viewModel呼び出す

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            TestViewModelTheme {
                Surface(modifier = Modifier.fillMaxSize()) {
                    Column(
                        horizontalAlignment = Alignment.CenterHorizontally,
                        verticalArrangement = Arrangement.Center,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(16.dp)
                    ) {
                        val spaceHeight = 20.dp
                        var isShowText by remember { mutableStateOf(false) } //ローカル変数

                        CustomTextField(
                            value = viewModel.firstName, //viewModel管理の状態を使用
                            onValueChange = { viewModel.firstName = it }, //viewModel管理の状態を書き換え
                            modifier = Modifier.fillMaxWidth(),
                            labelText = "氏名",
                            placeholder = { Text("氏名を入力してください") },
                        )
...
```

```Kotlin.TestViewModel.kt

class TestViewModel : ViewModel() { //ViewModelを継承するだけで作れるよ~
    var lastName by mutableStateOf("")
    var firstName by mutableStateOf("")
    var fullName by mutableStateOf("")

    fun createFullName() {
        if (lastName.isEmpty() || firstName.isEmpty()) {
            fullName = "名前はまだない"
            return
        }
        fullName = "$firstName $lastName"
    }
}
```

## 便利なボタン作ってみた
動画を見てると sealed classと言うものが使われていました。調べてみるとenumのようなものですね。(Flutterにも普通にあったらしい)
enumと違ってジェネリクスにできたり、動的に引数を渡せたり、使いやすそうなのでこれから多用していきます！

<img weight="300" alt="スクリーンショット 2024-06-28 18.32.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3826022/d20d2439-6f35-4b8f-337d-7715c5a93df8.png">

せっかく勉強したのでsealed classを使って
- ボタンの種類
    - NORMAL
    - OUTLINE
    - ROUNDED
- ボタンの大きさ
    - SMALL
    - MEDIUM
    - LARGE

の計９種類を組み合わせるだけで上の画像のようなボタンが作れちゃいます〜

```Kotlin.AppButton.kt
sealed class ButtonVariant(
    val cornerRadius: Int,
    val containerColor: Color,
) {
    data object NORMAL : ButtonVariant(0, Color(0xFFF85F6A))
    data object OUTLINE : ButtonVariant(0, Color(0xFFF85F6A))
    data object ROUNDED : ButtonVariant(50, Color(0xFFF85F6A))
}

sealed class ButtonSize(
    val height: Int,
    val weight: Int,
) {
    data object SMALL : ButtonSize(32, 80)
    data object MEDIUM : ButtonSize(40, 96)
    data object LARGE : ButtonSize(48, 112)
}

@Composable
fun AppButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    variant: ButtonVariant = ButtonVariant.NORMAL,
    size: ButtonSize = ButtonSize.MEDIUM,
) {
    val buttonColors = when (variant) {
        ButtonVariant.NORMAL -> ButtonDefaults.buttonColors(containerColor = variant.containerColor)
        ButtonVariant.OUTLINE -> ButtonDefaults.outlinedButtonColors()
        ButtonVariant.ROUNDED -> ButtonDefaults.buttonColors(containerColor = variant.containerColor)
    }

    val buttonShape = when (variant) {
        ButtonVariant.NORMAL, ButtonVariant.OUTLINE -> RoundedCornerShape(variant.cornerRadius)
        ButtonVariant.ROUNDED -> RoundedCornerShape(variant.cornerRadius)
    }

    val buttonModifier = when (size) {
        is ButtonSize.SMALL -> modifier.height(size.height.dp).width(size.weight.dp)
        is ButtonSize.MEDIUM -> modifier.height(size.height.dp).width(size.weight.dp)
        is ButtonSize.LARGE -> modifier.height(size.height.dp).width(size.weight.dp)
    }


    when (variant) {
        ButtonVariant.OUTLINE -> OutlinedButton(
            onClick = onClick,
            modifier = buttonModifier,
            shape = buttonShape,
            colors = buttonColors,
        ) {
            Text(text = "Click me")

        }
        else -> Button(
            onClick = onClick,
            modifier = buttonModifier,
            shape = buttonShape,
            colors = buttonColors,
        ) {
            Text(text = "Click me")
        }
    }
}
```

使い方は
```Kotlin.MainActivity.kt
                        Row {
                            //variant: NORMAL size: SMALL
                            AppButton(
                                onClick = {},
                                size = ButtonSize.SMALL,
                            )
                            Spacer(modifier = Modifier.width(2.dp))
                            //variant: OUTLINE size: SMALL
                            AppButton(
                                onClick = {},
                                variant = ButtonVariant.OUTLINE,
                                size = ButtonSize.SMALL,

                                )
                            Spacer(modifier = Modifier.width(2.dp))
                            //variant: ROUNDED size: SMALL
                            AppButton(
                                onClick = {},
                                variant = ButtonVariant.ROUNDED,
                                size = ButtonSize.SMALL,
                            )
                        }
                        Spacer(modifier = Modifier.height(spaceHeight))

                        Row {
                            //variant: NORMAL size: MEDIUM
                            AppButton(
                                onClick = {},
                            )
                            Spacer(modifier = Modifier.width(2.dp))
                            //variant: OUTLINE size: MEDIUM
                            AppButton(
                                onClick = {},
                                variant = ButtonVariant.OUTLINE,
                            )
                            Spacer(modifier = Modifier.width(2.dp))
                            //variant: ROUNDED size: MEDIUM
                            AppButton(
                                onClick = {},
                                variant = ButtonVariant.ROUNDED,
                            )
                        }

                        Spacer(modifier = Modifier.height(spaceHeight))
                        Row {
                            //variant: NORMAL size: LARGE
                            AppButton(
                                onClick = {},
                                size = ButtonSize.LARGE,
                            )
                            Spacer(modifier = Modifier.width(2.dp))
                            //variant: OUTLINE size: LARGE
                            AppButton(
                                onClick = {},
                                variant = ButtonVariant.OUTLINE,
                                size = ButtonSize.LARGE,
                            )
                            Spacer(modifier = Modifier.width(2.dp))
                            //variant: ROUNDED size: LARGE
                            AppButton(
                                onClick = { },
                                variant = ButtonVariant.ROUNDED,
                                size = ButtonSize.LARGE,
                            )
                        }
```

アプリで使用するボタンを簡単にまとめることができます:relaxed:

## まとめ
知らない単語や知らない概念が多くて右も左もわかりませんが、これから頑張ってJetpackComposeの学習を続けていきたいと思います！
あと記事投稿も頑張ります笑

二日目の記事はHiltやRoomを使ったお話になります〜