【jetpack compose】自作アプリにダークモードを適用しよう

白い画面がまぶしいと感じるときに恋しくなるのがダークモードの黒。今回はこのダークモードを自作のアプリにも適用していきます。
# ベースになるアプリ

https://github.com/jtakumi/jetpackComposebasic/tree/viewModel

画像をクリックした回数をカウントするアプリで、viewModelを導入しているため画面を回転させても回数がリセットされないものです。

# 今回登場するツール

https://m3.material.io/theme-builder#/custom

↑ベースにする色を１色設定するだけでそのほかの色を勝手に設定してテーマカラーを作成してくれる最高のサイト🎨

- Color.kt
jetpack composeのプロジェクトを作成すると**自動生成されるカラー設定ファイル**

- Theme.kt
こちらも自動生成されるファイル。**テーマの設定**を行う

# 実際の動作の様子

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747809/e694c6ee-5f9c-1156-dd5b-843d76eca974.gif width=300>

# Material.ioでテーマカラーを作成する
テーマカラーのベースカラーは#19753Aにしてみます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747809/6636a99b-2ad3-8e2b-c70d-635de46de574.png>

すると以下のようにテーマカラーが自動的に決定しました。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2747809/56d7cc4d-88f0-1377-cdeb-46ab51619829.png width=600>

これをソースコードの中に入れていきます。

# ソースコード

主な流れは以下の３ステップです。
1. Color.ktでカラーコードを打ち込む
2. Theme.ktでテーマを設定
3. MainActivity.ktで画面に適用

---

### 1. Color.ktでカラーコードを打ち込む

サイトでは"#~"で表記されていますが、コードの中では"0xFF~"に変えて記述します。


```Color.kt

package com.example.jetpackcomposebasic.ui.theme

import androidx.compose.ui.graphics.Color


// # -> 0xFF
// ベースカラー = #19753a
//Light mode
val android_light_primary = Color(0xFF0b6d33)
val android_light_onPrimary = Color(0xFFffffff)
val android_light_primaryContainer = Color(0xFF9df6ad)
val android_light_onPrimaryContainer = Color(0xFF00210a)
val android_light_secondary = Color(0xFF506351)
val android_light_onSecondary = Color(0xFFffffff)
val android_light_secondaryContainer = Color(0xFFd3e8d2)
val android_light_onSecondaryContainer = Color(0xFF0e1f11)
val android_light_tertiary = Color(0xFF39656d)
val android_light_onTertiary = Color(0xFFffffff)
val android_light_tertiaryContainer = Color(0xFFbdeaf4)
val android_light_onTertiaryContainer = Color(0xFF001f25)
val android_light_error = Color(0xFFba1a1a)
val android_light_onError = Color(0xFFffffff)
val android_light_errorContainer = Color(0xFFffdad6)
val android_light_onErrorContainer = Color(0xFF410002)
val android_light_background = Color(0xFFfcfdf7)
val android_light_onBackground = Color(0xFF1a1c19)
val android_light_surface = Color(0xFFfcfdf7)
val android_light_onSurface = Color(0xFF1a1c19)
val android_light_surfaceVariant = Color(0xFFdde5da)
val android_light_onSurfaceVariant = Color(0xFF414941)
val android_light_outline = Color(0xFF727970)
val android_light_inverseOnSurface = Color(0xFFF9EFE7)
val android_light_inverseSurface = Color(0xFF34302A)
val android_light_inversePrimary = Color(0xFFFFB951)
val android_light_shadow = Color(0xFF000000)
val android_light_surfaceTint = Color(0xFF825500)
val android_light_outlineVariant = Color(0xFFD3C4B4)
val android_light_scrim = Color(0xFF000000)

//Dark mode
val android_dark_primary = Color(0xFF82d993)
val android_dark_onPrimary = Color(0xFF003917)
val android_dark_primaryContainer = Color(0xFF005224)
val android_dark_onPrimaryContainer = Color(0xFF9df6ad)
val android_dark_secondary = Color(0xFFb7ccb6)
val android_dark_onSecondary = Color(0xFF233425)
val android_dark_secondaryContainer = Color(0xFF394b3b)
val android_dark_onSecondaryContainer = Color(0xFFd3e8d2)
val android_dark_tertiary = Color(0xFFa1ced8)
val android_dark_onTertiary = Color(0xFF00363e)
val android_dark_tertiaryContainer = Color(0xFF204d55)
val android_dark_onTertiaryContainer = Color(0xFFbdeaf4)
val android_dark_error = Color(0xFFffb4ab)
val android_dark_onError = Color(0xFF690005)
val android_dark_errorContainer = Color(0xFF93000a)
val android_dark_onErrorContainer = Color(0xFFffdad6)
val android_dark_background = Color(0xFF1a1c19)
val android_dark_onBackground = Color(0xFFe2e3de)
val android_dark_surface = Color(0xFF1a1c19)
val android_dark_onSurface = Color(0xFFe2e3de)
val android_dark_surfaceVariant = Color(0xFF414941)
val android_dark_onSurfaceVariant = Color(0xFFc1c9be)
val android_dark_outline = Color(0xFF8b9389)
val android_dark_inverseOnSurface = Color(0xFF1F1B16)
val android_dark_inverseSurface = Color(0xFFEAE1D9)
val android_dark_inversePrimary = Color(0xFF825500)
val android_dark_shadow = Color(0xFF000000)
val android_dark_surfaceTint = Color(0xFFFFB951)
val android_dark_outlineVariant = Color(0xFF4F4539)
val android_dark_scrim = Color(0xFF000000)

val seed = Color(0xFF825500)

```

---

### 2. Theme.ktでテーマを設定

Color.ktで打ち込んだ色をテーマとして適用します。

```Theme.kt

package com.example.jetpackcomposebasic.ui.theme

import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat


//Light mode テーマ
private val LightColorScheme = lightColorScheme(
    primary = android_light_primary,
    onPrimary = android_light_onPrimary,
    primaryContainer = android_light_primaryContainer,
    onPrimaryContainer = android_light_onPrimaryContainer,
    secondary = android_light_secondary,
    onSecondary = android_light_onSecondary,
    secondaryContainer = android_light_secondaryContainer,
    onSecondaryContainer = android_light_onSecondaryContainer,
    tertiary = android_light_tertiary,
    onTertiary = android_light_onTertiary,
    tertiaryContainer = android_light_tertiaryContainer,
    onTertiaryContainer = android_light_onTertiaryContainer,
    error = android_light_error,
    errorContainer = android_light_errorContainer,
    onError = android_light_onError,
    onErrorContainer = android_light_onErrorContainer,
    background = android_light_background,
    onBackground = android_light_onBackground,
    surface = android_light_surface,
    onSurface = android_light_onSurface,
    surfaceVariant = android_light_surfaceVariant,
    onSurfaceVariant = android_light_onSurfaceVariant,
    outline = android_light_outline,
    inverseOnSurface = android_light_inverseOnSurface,
    inverseSurface = android_light_inverseSurface,
    inversePrimary = android_light_inversePrimary,
    surfaceTint = android_light_surfaceTint,
    outlineVariant = android_light_outlineVariant,
    scrim = android_light_scrim,
)


//Dark mode テーマ
private val DarkColorScheme = darkColorScheme(
    primary = android_dark_primary,
    onPrimary = android_dark_onPrimary,
    primaryContainer = android_dark_primaryContainer,
    onPrimaryContainer = android_dark_onPrimaryContainer,
    secondary = android_dark_secondary,
    onSecondary = android_dark_onSecondary,
    secondaryContainer = android_dark_secondaryContainer,
    onSecondaryContainer = android_dark_onSecondaryContainer,
    tertiary = android_dark_tertiary,
    onTertiary = android_dark_onTertiary,
    tertiaryContainer = android_dark_tertiaryContainer,
    onTertiaryContainer = android_dark_onTertiaryContainer,
    error = android_dark_error,
    errorContainer = android_dark_errorContainer,
    onError = android_dark_onError,
    onErrorContainer = android_dark_onErrorContainer,
    background = android_dark_background,
    onBackground = android_dark_onBackground,
    surface = android_dark_surface,
    onSurface = android_dark_onSurface,
    surfaceVariant = android_dark_surfaceVariant,
    onSurfaceVariant = android_dark_onSurfaceVariant,
    outline = android_dark_outline,
    inverseOnSurface = android_dark_inverseOnSurface,
    inverseSurface = android_dark_inverseSurface,
    inversePrimary = android_dark_inversePrimary,
    surfaceTint = android_dark_surfaceTint,
    outlineVariant = android_dark_outlineVariant,
    scrim = android_dark_scrim,
)

@Composable
fun JetpackComposeBasicTheme(
    useDarkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable() () -> Unit
) {
    //システムのダークモードがONであればダークモードを適用
    val currentColors = if (useDarkTheme) {
        DarkColorScheme
    } else {
        LightColorScheme
    }
    val currentView = LocalView.current
    if (currentView.isInEditMode.not()) {
        SideEffect {
            val contextActivity = (currentView.context as Activity).window
            contextActivity.statusBarColor = currentColors.primary.toArgb()
            WindowCompat.getInsetsController(
                contextActivity,
                currentView
            ).isAppearanceLightStatusBars =
                useDarkTheme
        }
    }

    MaterialTheme(
        typography = Typography,
        colorScheme = currentColors,
        content = content
    )
}

```

---

### 3. MainActivity.ktで画面に適用

Theme.ktで設定したテーマを画面に適用します。

```MainActivity.kt
package com.example.jetpackcomposebasic

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.jetpackcomposebasic.ui.theme.JetpackComposeBasicTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Surface {
            //テーマを適用する
                JetpackComposeBasicTheme {
                    Column {
                    //クリックする画像を表示する関数
                    //今回は内容の記述省略
                        ShowImage()
                    }
                }
            }
        }
    }
}

```

以上でダークモードを使用することができます。

# Repository

https://github.com/jtakumi/jetpackComposebasic/tree/theme_change