“This is the ninth day of my participation in the First Challenge 2022. For details: First Challenge 2022”

The default Material theme for the new Compose project gives us some color, but it’s not enough for a flowery person like me. Therefore, you can configure the theme if the theme provided by the system cannot meet the requirements

For Compose, skinning is simple

The XML method was a little bit more complicated before loading the XML layout with a LayoutInflater call to the inflate method, which has a createViewFromTag inside it, The CreateView method is used to load a LayoutInflater based on the Factory interface type (Factory or Factory2). Then get all the attributes of the control through AttributeSet and finally switch the background color

This is the default code, so we’re going to change it

color.kt

Start by writing a set of color variables using global static variables

val statusBarColorLight = Color(0xFFFFFFFF)
val statusBarColorDark = Color(0xFF1C1C28)

val backgroundColorLight = Color(0xFFF2F2F6)
val backgroundColorDark = Color(0xFF1C1C28)

val textPrimaryLight = Color(0xFF333333)
val textPrimaryDark = Color(0xFFE8E8F0)

val textSecondaryLight = Color(0xFF999999)
val textSecondaryDark = Color(0xFF999999)... Is omitted500Word ha haCopy the code

Theme.kt

Define various color names

@Stable
class AppColors(
    statusBarColor: Color,
    themeUi: Color,
    background: Color,
    listItem: Color,
    divider: Color,
    textPrimary: Color,
    textSecondary: Color,
    mainColor: Color,
    card: Color,
    icon: Color,
    info: Color,
    warn: Color,
    success: Color,
    error: Color,
    primaryBtnBg: Color,
    secondBtnBg: Color,
    hot: Color,
    placeholder: Color,
)
Copy the code

Then introduce mutableStateOf to indicate that the Color is stateful. If the state changes, all controls that reference the Color will change and need to be redrawn.

var statusBarColor: Color by mutableStateOf(statusBarColor)
    internal set
var themeUi: Color by mutableStateOf(themeUi)
    internal set
var background: Color by mutableStateOf(background)
    private set
var listItem: Color by mutableStateOf(listItem)
    private set
var divider: Color by mutableStateOf(divider)
    private set
var textPrimary: Color by mutableStateOf(textPrimary)
    internal set
var textSecondary: Color by mutableStateOf(textSecondary)
    private set
var mainColor: Color by mutableStateOf(mainColor)
    internal set
var card: Color by mutableStateOf(card)
    private set
var icon: Color by mutableStateOf(icon)
    private set
var info: Color by mutableStateOf(info)
    private set
var warn: Color by mutableStateOf(warn)
    private set
var success: Color by mutableStateOf(success)
    private set
var error: Color by mutableStateOf(error)
    private set
var primaryBtnBg: Color by mutableStateOf(primaryBtnBg)
    internal set
var secondBtnBg: Color by mutableStateOf(secondBtnBg)
    private set
var hot: Color by mutableStateOf(hot)
    private set
var placeholder: Color by mutableStateOf(placeholder)
    private set
Copy the code

Just copy and paste it

Then define two sets of themes day and night you will never understand my sorrow like the day does not understand the darkness of the night

Private Val DarkColorPalette = AppColors(statusBarColor = statusBarColorDark, themeUi = themeColor, background = backgroundColorDark, listItem = listItemDark, divider = dividerDark, textPrimary = textPrimaryDark, textSecondary = textSecondaryDark, mainColor = black3, card = black3, icon = grey1, info = info, warn = warn, success = green3, error = red2, primaryBtnBg = backgroundColorDark, secondBtnBg = black3, hot = red, Placeholder = grey1,) private val LightColorPalette = AppColors(statusBarColor = statusBarColorLight, themeUi = themeColor, background = backgroundColorLight, listItem = listItemLight, divider = dividerLight, textPrimary = textPrimaryLight, textSecondary = textSecondaryLight, mainColor = white, card = white1, icon = inonGary, info = info, warn = warn, success = green3, error = red2, primaryBtnBg = themeColor, secondBtnBg = white3, hot = red, placeholder = white3, )Copy the code

Now comes the important step, how do you apply these colors?

@Composable
fun AppTheme(
    content: @Composable () -> Unit
)
Copy the code

That’s it. You just need to put the controls inside when you use them

So before we apply it we need to decide which theme to use and here I’m using dark mode to demonstrate that in the Composable we can use this line of code to determine that the system is in dark mode

isSystemInDarkTheme()
Copy the code
Var LocalAppColors = compositionLocalOf {LightColorPalette} @stable Object CustomTheme {val colors: AppColors @composable get() = LocalAppColors. Current Enum Class Theme {Light, Dark}}Copy the code

About compositionLocalOf

Compose explicitly passes the data through the composition tree to the composable function via arguments. This is often the easiest and best way to let data flow through the tree.

Sometimes, this model can be cumbersome or disaggregated for data required by many components, or when components need to pass data between each other while keeping the implementation details private. In these cases, CompositionLocal can be used as an implicit way to let data flow through the composition.

CompositionLocal is hierarchical in nature. They make sense when CompositionLocal needs to restrict the values to a particular subhierarchy of the composition.

You must create a CompositionLocal instance that can be statically referenced by consumers. The CompositionLocal instance does not hold any data itself and can be considered a type-safe identifier for the data passed into the tree. The CompositionLocal factory function takes a single argument: creates a factory with default values if a is used in the absence of a CompositionLocal provider. If this is a case you don’t want to handle, you can throw an error in this factory.

Somewhere in the tree, CompositionLocalProvider can use a component called CompositionLocal. This is usually at the “root” of the tree, but can be anywhere, and can be used in multiple places to override the supplied values of the subtree. Intermediate components do not need to know about this CompositionLocal value and can have zero dependencies on it

The complete code

@Composable
fun AppTheme(
    isDark :Boolean = isSystemInDarkTheme(),
    content: @Composable() - >Unit
) {

    val targetColors = if (isDark) DarkColorPalette else LightColorPalette

    val statusBarColor = animateColorAsState(targetColors.statusBarColor, TweenSpec(600))
    val themeUi = animateColorAsState(targetColors.themeUi, TweenSpec(600))
    val background = animateColorAsState(targetColors.background, TweenSpec(600))
    val listItem = animateColorAsState(targetColors.listItem, TweenSpec(600))
    val divider = animateColorAsState(targetColors.divider, TweenSpec(600))
    val textPrimary = animateColorAsState(targetColors.textPrimary, TweenSpec(600))
    val textSecondary = animateColorAsState(targetColors.textSecondary, TweenSpec(600))
    val mainColor = animateColorAsState(targetColors.mainColor, TweenSpec(600))
    val card = animateColorAsState(targetColors.card, TweenSpec(600))
    val icon = animateColorAsState(targetColors.icon, TweenSpec(600))
    val info = animateColorAsState(targetColors.info, TweenSpec(600))
    val warn = animateColorAsState(targetColors.warn, TweenSpec(600))
    val success = animateColorAsState(targetColors.success, TweenSpec(600))
    val error = animateColorAsState(targetColors.error, TweenSpec(600))
    val primaryBtnBg = animateColorAsState(targetColors.primaryBtnBg, TweenSpec(600))
    val secondBtnBg = animateColorAsState(targetColors.secondBtnBg, TweenSpec(600))
    val hot = animateColorAsState(targetColors.hot, TweenSpec(600))
    val placeholder = animateColorAsState(targetColors.placeholder, TweenSpec(600))

    val appColors = AppColors(
        statusBarColor = statusBarColor.value,
        themeUi = themeUi.value,
        background = background.value,
        listItem = listItem.value,
        divider = divider.value,
        textPrimary = textPrimary.value,
        textSecondary = textSecondary.value,
        mainColor = mainColor.value,
        card = card.value,
        icon = icon.value,
        primaryBtnBg = primaryBtnBg.value,
        secondBtnBg = secondBtnBg.value,
        info = info.value,
        warn = warn.value,
        success = success.value,
        error = error.value,
        hot = hot.value,
        placeholder = placeholder.value
    )

    ProvideWindowInsets {
        CompositionLocalProvider(LocalAppColors provides appColors) {
            MaterialTheme(
                shapes = shapes
            ) {
                ProvideWindowInsets(content = content)
            }
        }
    }
}
Copy the code

TweenSpec is used to create effects that are configured for a given duration, delay, and relief curve so that peels don’t blink and switch slowly

Just use it under the AppTheme