start

Application skin change, this is really a cliche issue, from the original Android, to the later Flutter, and now Compose, although it is a cliche, in fact, it is still a new bottle of old wine.

Android native theme switch is not mentioned here, this is not the focus of this article, and that article is estimated to be unable to say 😂.

The theme switch of Flutter relies mainly on provider state management. In fact, the theme switch of Flutter is similar in Compose.

The GitHub address is at the bottom of the article. Let’s take a look at the implementation:

after

For example, you can use colors from the MaterialTheme instead of hard-coding, as well as shapes, Typography, and so on.

Compose is a topic that you should be familiar with, but there are a few that aren’t, so let’s talk about it briefly.

Topic in Compose

When you create a new Compose project, the system will automatically create the following structure for you:

It can be seen that the system created a total of four files, as the name implies, respectively: color, shape, theme, type, this article we mainly look at the color and theme.

Let’s look at what Color files look like by default:

val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
Copy the code

This is the code in the Color file, which simply defines four colors.

Let’s look at the Theme file again:

private val DarkColorPalette = darkColors(
    primary = Purple200,
    primaryVariant = Purple700,
    secondary = Teal200
)
​
private val LightColorPalette = lightColors(
    primary = Purple500,
    primaryVariant = Purple700,
    secondary = Teal200
)
​
@Composable
fun PlayAndroidTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }
​
    MaterialTheme(
        colors = colors,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}
Copy the code

You can see that the Theme defines dark and light colors, and then dynamically ADAPTS them by determining if they are night mode.

Let’s look at the lightColors method:

fun lightColors(
    primary: Color = Color(0xFF6200EE),
    primaryVariant: Color = Color(0xFF3700B3),
    secondary: Color = Color(0xFF03DAC6),
    secondaryVariant: Color = Color(0xFF018786),
    background: Color = Color.White,
    surface: Color = Color.White,
    error: Color = Color(0xFFB00020),
    onPrimary: Color = Color.White,
    onSecondary: Color = Color.Black,
    onBackground: Color = Color.Black,
    onSurface: Color = Color.Black,
    onError: Color = Color.White
): Colors
Copy the code

As you can see from the code above, each color value can be set here, and the shade mode is similar, and can be set.

Use the theme

The topic in Compose is briefly described above. How should you use the topic once it is composed? Look at the code:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState) setContent {// PlayAndroidTheme {NavGraph()}}}Copy the code

Yes, it’s as simple as that, just need to set the theme in the outer layer of the page. Now that the theme is set, how do I use the colors I just set to the theme? Or other resources? The code:

/ / color used MaterialTheme. The colors. The primary / / shape using MaterialTheme shapes. The large / / font type use MaterialTheme typography. Body1Copy the code

Remember that you need to comply with the Compose specification, which means that you should use the Compose specification as much as possible when using these resources.

To solve

How to switch Topics

First of all, we need to think about how to switch the theme. The whole theme must be used at the beginning of the project — start the Activity, but the page for switching the theme must be different. Then how to let the Activity know after switching the theme page?

At the beginning, my idea was not Compose enough. I wanted to use broadcast, send a broadcast after clicking on the theme page, receive it in the Activity, and refresh it after receiving it. I did, and the functionality worked, but something didn’t feel right, and that’s not the way it should be in Compose.

When I was having dinner in the canteen at noon, IT suddenly occurred to me that all UI changes in Compose are driven by state. I directly set the theme switch to one state.

Open to

Let’s go ahead and set up our default themes:

// gray const val GRAY_THEME = 1 // dark blue const val DEEP_BLUE_THEME = 2 // green const val SKY_BLUE_THEME = 0 // gray const val GRAY_THEME = 1 // dark blue const val DEEP_BLUE_THEME = 2 // GREEN_THEME = 3 // purple const val PURPLE_THEME = 4 // orange const val ORANGE_THEME = 5 // brown const val BROWN_THEME = 6 // red Const val RED_THEME = 7 // const val MAGENTA_THEME = 9 as a magenta const val CYAN_THEME = 8Copy the code

I’m not using enumerations for the sake of simplicity, but it’s pretty much the same thing.

Then add a topic’s state:

/** * theme state */ val themeTypeState: MutableState<Int> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { mutableStateOf(getDefaultThemeId()) }Copy the code

There is a method called getDefaultThemeId in the theme state to get the default theme ID. Take a look:

/ * * * the default theme for current * / fun getDefaultThemeId () : Int = DataStoreUtils. GetSyncData (CHANGED_THEME SKY_BLUE_THEME)Copy the code

DataStore is used here to access the data, and DataStore is also a member of Jetpack. For those interested, check out my previous article: Hug DataStore again

Then modify the theme method:

@Composable
fun PlayAndroidTheme(
    themeId: Int = 0,
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) {
        playDarkColors()
    } else {
        getThemeForThemeId(themeId)
    }
​
    MaterialTheme(
        colors = colors,
        typography = typography,
        shapes = Shapes,
        content = content
    )
}
Copy the code

The getThemeForThemeId method is used to obtain the desired theme color set from the themeId.

Private fun getThemeForThemeId(themeId: themeId); Int) = when (themeId) { SKY_BLUE_THEME -> { playLightColors( primary = primaryLight ) } GRAY_THEME -> { playLightColors( Primary = gray_theme)}Copy the code

Next, modify the call in the Activity:

PlayAndroidTheme(themeTypeState.value) {
    NavGraph()
}
Copy the code

Because themeTypeState is a State, the UI is automatically refreshed when its value changes.

finishing

Creating a List of topics

To prepare, let’s create an entity class to store topic information:

data class ThemeModel(val color: Color, val colorId: Int, val colorName: String)
Copy the code

Let’s put the topics we added into a List:

Private val themeList = arrayListOf(ThemeModel(primaryLight, SKY_BLUE_THEME, "Sky blue "), ThemeModel(gray_theme, gray_theme, "gray "), ThemeModel(deep_blue_theme, deep_blue_theme," dark blue "), ThemeModel(green_theme, GREEN_THEME, "green "), ThemeModel(purple_theme, purple_theme," purple "), ThemeModel(orange_theme, orange_theme, "orange "), ThemeModel(brown_theme, brown_theme, "brown "), ThemeModel(red_theme, red_theme," red "), ThemeModel(cyan_theme, cyan_theme, "Cyan "), ThemeModel(magenta_theme, magenta_theme," magenta "),Copy the code

Create a theme switch page

All things are ready except the east wind. Now that the theme block is ready, you just need to create another theme switch page, save the theme ID and refresh the value of themeTypeState when clicked.

As you can see from the diagram above, it is best to use the LazyVerticalGrid for this layout, and then set the next row to put five items:

LazyVerticalGrid(
    cells = GridCells.Fixed(5),
    modifier = Modifier.padding(horizontal = 10.dp)
) {
    items(themeList) { item: ThemeModel ->
        ThemeItem(playTheme, item) {
            val playTheme = item.colorId
            themeTypeState.value = playTheme
            DataStoreUtils.putSyncData(CHANGED_THEME, playTheme)
        }
    }
}
Copy the code

OK, that’s it, that’s it, that’s it, that’s the Gif at the beginning of this article.

The ending

So soon to the end, still a little sad, ha ha ha. Github address is here: github.com/zhujiang521…

Don’t forget to switch branches: compose

For example, if you want to learn about Compose in a more systematic way, you can check out my new book, Jetpack Compose: New UI Programming for Android, which contains the entire Compose framework.

Purchase address of JINGdong

Dangdang Purchase address

If it helps you, don’t forget to hit the Star. Thank you very much.

In fact, there are some details that I haven’t covered, so if you have any questions, please feel free to ask in the comments section.

Well, I’ll stop here first, goodbye!