A Material Design 3 Theming Tutorial

This chapter will demonstrate how to migrate an Android Studio project from Material Design 2 to Material Design 3 and create a new theme using the Material Theme Builder tool. The tutorial will also demonstrate how to add support for and test dynamic theme colors.

Creating the ThemeDemo project

Launch Android Studio and create a new Empty Compose Activity project named ThemeDemo, specifying com.example.themedemo as the package name, and selecting a minimum API level of API 26: Android 8.0 (Oreo). Within the MainActivity.kt file, delete the Greeting function and add a new empty composable named MainScreen:

@Composable
fun MainScreen() {
    
}

Next, edit the onCreateActivity() method and DefaultPreview function to call MainScreen instead of Greeting and enable the system UI preview option:

@Preview(showBackground = true, showSystemUi = true)
@Composable
fun DefaultPreview() {
.
.

Adding the Material Design 3 library

Assuming that you are running a version of Android Studio that defaults to Material Design 2, the first step in this project is to add the Material Design 3 library to the build configuration. Within the Project tool window, locate and open the module level build.gradle file (app -> build -> build.gradle (Module: ThemeDemo)) and add the library as follows (keeping in mind that a more recent version of the library may now be available):

dependencies {
    implementation "androidx.compose.material3:material3:1.0.0-alpha04"
.
.

After making the change, click on the Sync Now link to apply the new build configuration to the project.

 

You are reading a sample chapter from Jetpack Compose 1.2 Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

Designing the user interface

The main activity will contain a simple layout containing some of the components provided with the MD3 alpha release that will enable us to see the effect of theming work performed later in the chapter. For the latest information on which MD3 components are available for use with Jetpack Compose, refer to the following web page:

https://developer.android.com/jetpack/androidx/releases/compose-material3

Within the MainActivity.kt file, edit the MainScreen composable so that it reads as follows, including the OptIn annotation to enable the use of the experimental MD3 API. Also, delete any MD2 import directives:

.
.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
.
.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen() {
 
    var selectedItem by remember { mutableStateOf(0) }
    val items = listOf("Home", "Settings", "Favorites")
    val icons = listOf(Icons.Filled.Home, Icons.Filled.Settings, 
                                  Icons.Filled.Favorite)
 
    Column(
        verticalArrangement = Arrangement.SpaceBetween, 
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
 
        SmallTopAppBar(title = { Text("ThemeDemo") }, scrollBehavior = null)
 
        Button(onClick = { }) {
            Text("MD3 Button")
        }
 
        Text("A Theme Demo")
 
        FloatingActionButton(onClick = { }) {
                Text("FAB")
        }
 
        NavigationBar {
            items.forEachIndexed { index, item ->
                NavigationBarItem(
                    icon = { Icon(icons[index], contentDescription = null) },
                    label = { Text(item) },
                    selected = selectedItem == index,
                    onClick = { selectedItem = index }
                )
            }
        }
    }
}

When previewed, the MainScreen layout should appear as illustrated in Figure 50-1:

Figure 50-1

 

You are reading a sample chapter from Jetpack Compose 1.2 Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

The completed design is currently using default theme colors and fonts. The next step is to build an entirely new theme for the app.

Building a new theme

The theme for the project will be designed and generated using the Material Theme Builder. Open a browser window and navigate to the following URL to access the builder tool:

https://material-foundation.github.io/material-theme-builder/

Once you have loaded the builder, select the Custom button at the top of the screen and then click on the Primary color block in the Key Colors section to display the color selector. From the color selector, choose any color you feel like using as the basis for your theme:

Figure 50-2

 

You are reading a sample chapter from Jetpack Compose 1.2 Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

Review the color scheme in the My Theme panel and make any necessary color adjustments using the Color Key until you are happy with the color slots. Once the theme is ready, click on the Export button in the top right-hand corner and select the Jetpack Compose (Theme.kt) option. When prompted, save the file to a suitable location on your computer filesystem. The theme will be saved as a compressed file named material-theme.zip.

Using the appropriate tool for your operating system, unpack the theme file which should contain the following files in a folder with the path material-theme/ui/theme:

  • Color.kt
  • Theme.kt
  • Type.kt

Now that the theme files have been generated, they need to be integrated into the Android Studio project.

Adding the theme to the project

Before we can add the new theme to the project we first need to remove the old MD2 theme files. Within the Android Studio Project tool window, select and delete the Color.kt, Theme.kt, and Type.kt files from the ui.theme folder. Once the files have been removed, locate the MD3 theme files in the material-theme folder on your local filesystem and drag and drop them onto the ui.theme folder in the Project tool window:

Figure 50-3

 

You are reading a sample chapter from Jetpack Compose 1.2 Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

After adding the files, edit each one in turn and change the package declaration to match the current project which, assuming you followed the steps at the start of the chapter, will read as follows:

package com.example.themedemo.ui.theme 

Next, edit the Theme.kt and change the name of the Theme composable from AppTheme to ThemeDemoTheme:

@Composable
fun ThemeDemoTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
    LightThemeColors
} else {
    DarkThemeColors
.
.

Return to the MainActivity.kt file and refresh the Preview panel to confirm that the components are rendered using the new theme. Take some time to explore the Colors.kt, Type.kt, and Theme.kt files to see the different available theme settings. Also, experiment by making changes to different typography and color values.

Enabling dynamic colors

To test dynamic colors the app will need to be run on a device or emulator running Android 12 or later with the correct Wallpaper settings. On the device or emulator, launch the Settings app and select Wallpaper & style from the list of options. On the wallpaper settings screen click the option to change the wallpaper (marked A in Figure 50-4) and select a wallpaper image containing colors that differ significantly from the colors in your theme. Once selected, assign the wallpaper to the Home screen.

Return to the Wallpaper & styles screen and make sure that the Wallpaper colors option is selected (B) before trying out the different color scheme buttons (C). As each option is clicked the wallpaper example will change to reflect the selection:

 

You are reading a sample chapter from Jetpack Compose 1.2 Essentials. Buy the full book now in Print or eBook format. Learn more.

Preview  Buy eBook  Buy Print

 

Figure 50-4

Once you have made a choice, return to Android Studio, load the Theme.kt file into the code editor and make the following changes to the ThemeDemoTheme composable to add support for dynamic colors:

.
import android.os.Build
import androidx.compose.material3.*
import androidx.compose.ui.platform.LocalContext
.
.
@Composable
fun ThemeDemoTheme(
    useDarkTheme: Boolean = isSystemInDarkTheme(),
        content: @Composable() () -> Unit
) {
 
    val useDynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
 
    val colors = when {
        useDynamicColor && useDarkTheme ->
            dynamicDarkColorScheme(LocalContext.current)
              useDynamicColor && !useDarkTheme ->
                dynamicLightColorScheme(LocalContext.current)
        useDarkTheme -> DarkThemeColors
            else -> LightThemeColors
    }
 
    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}

Build and run the app and note that the layout is now using a theme that matches the wallpaper color. Place the ThemeDemo app into the background, return to the Wallpaper & styles settings screen and choose a different wallpaper. Bring the ThemeDemo app to the foreground again at which point it will have dynamically adapted to match the new wallpaper.

Summary

In this chapter, we have demonstrated how to migrate an Android Studio project from Material Design 2 to Material Design 3. The project also made use of the Material Theme Builder to design a new theme and explained the steps to integrate the generated theme files into a project. Finally, the chapter showed how to implement and use the Material Me dynamic colors feature of Android 12.