Jetpack Compose中多主题设置

前言

开发APP的时候我们可能使用不同的主题,甚至不同主题下的布局也可能发生改变,这样我们就需要在项目创建的时候生成的主题上进行修改来满足我们的需求。

https://developer.android.google.cn/jetpack/compose/designsystems/material3?hl=zh-cn

颜色文件

Color.kt

1
2
3
4
5
6
7
8
9
import androidx.compose.ui.graphics.Color

val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)

val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

主题

Theme.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)

private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
)

// 定义主题
enum class AppTheme {
LIGHT,
DARK
}

// 创建一个 CompositionLocal,用于在应用程序中跟踪当前的主题
val LocalAppTheme = compositionLocalOf<AppTheme> {
error("No AppTheme provided")
}

@Composable
fun ZAppTheme(
theme: AppTheme = if (isSystemInDarkTheme()) AppTheme.DARK else AppTheme.LIGHT,
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (theme == AppTheme.DARK) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}

theme == AppTheme.DARK -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = theme == AppTheme.DARK
}
}
val appTheme = remember { theme }
CompositionLocalProvider(LocalAppTheme provides appTheme) {
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
}

这里说明几点:

  1. 这里定义枚举是为了支持更多的主题。
  2. 使用MaterialTheme是为了引用的扁平化组件能跟随主题改变样式。
  3. 定义LocalAppTheme是为了实现不同的主题可以显示不同的布局。

组件中判断使用的主题

1
2
3
4
5
6
7
8
// 使用主题
@Composable
fun MyComposable() {
// 在这里使用当前的主题进行布局
if(LocalAppTheme.current == AppTheme.DARK){

}
}

使用主题的颜色

1
MaterialTheme.colorScheme.background

使用主题的文本样式

1
MaterialTheme.typography.labelLarge

使用主题的形状样式

1
MaterialTheme.shapes.medium

说明

CompositionLocalProvider

CompositionLocalProvider 是 Jetpack Compose 中的一个重要概念,用于向整个组合树(Compose tree)提供特定类型的值。

在 Compose 中,组合树是由各种组合函数(如 @Composable 标记的函数)构成的层级结构,用于描述应用程序的 UI 层次结构。

CompositionLocalProvider 的作用是将特定类型的值提供给其子组件。这些值在组合树中被视为局部值,并可以被任何子组件使用,而不需要通过显式参数传递。这使得在整个应用程序中共享某些数据变得非常方便,尤其是对于主题、本地化设置、用户身份验证状态等方面的信息。

在给定的组合函数中,可以通过 LocalXXX.current 来获取 CompositionLocalProvider 提供的值,其中 XXX 是您定义的类型。

这使得在组合树中任何地方都能够访问到这些值,而不需要手动传递它们。

总的来说,CompositionLocalProvider 用于在组合树中传递局部值,使得这些值对于整个组合树中的任何组件都可用,而不需要显式传递。

Material3可配置的ColorScheme

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ColorScheme(
primary: Color,
onPrimary: Color,
primaryContainer: Color,
onPrimaryContainer: Color,
inversePrimary: Color,
secondary: Color,
onSecondary: Color,
secondaryContainer: Color,
onSecondaryContainer: Color,
tertiary: Color,
onTertiary: Color,
tertiaryContainer: Color,
onTertiaryContainer: Color,
background: Color,
onBackground: Color,
surface: Color,
onSurface: Color,
surfaceVariant: Color,
onSurfaceVariant: Color,
surfaceTint: Color,
inverseSurface: Color,
inverseOnSurface: Color,
error: Color,
onError: Color,
errorContainer: Color,
onErrorContainer: Color,
outline: Color,
outlineVariant: Color,
scrim: Color,
)

浅色的配色

m3-light