Exploring Material You for Jetpack Compose

In this post, we’ll look at the new Material You library available for Jetpack Compose and see how we can apply its dynamic colors to our Chat Compose SDK for a fun, colorful, and personalized messaging experience.

Jaewoong E.
Márton B.
Jaewoong E. & Márton B.
Published January 10, 2022 Updated January 11, 2022
Exploring Material You for Jetpack Compose

You'll learn all about Material You and how you can dynamically import colors from the Material 3 Compose library into our versatile Chat Compose SDK, giving your app a more polished and personalized feel for your users.

To show you just how powerful the API is, you'll also learn how to use the Material Theme Builder so that you can create your own Material Design 3 themes.

What Is Material You?

Earlier this year, Google announced Material You, a new direction for Material Design that focuses on personalization and flexibility.

A major element of Material You is its dynamic color system, which is a color extraction algorithm that enables you to build a color scheme based on a user's wallpaper. On Android 12, both the system UI and individual applications can use color schemes based on the dynamic color system to provide an individualized and expressive experience.

In late October, the new guidelines, components, and APIs were finally released to enable developers to start building with Material Design 3.

The guidelines walk you through how to implement dynamic color and explore new personalization features of Material Design 3. If you're interested in dynamic colors, check out the links below:

The Compose Material 3 Library

If you're building apps with Jetpack Compose, you can access APIs and components for Material 3 using the aptly-named Compose Material 3 library.

This library contains updated components, typography, color, elevation APIs, and more. In this post, we'll only focus on the new ColorScheme class and dynamic colors.

However, you can take a look at the Material Theming in Compose page or the accompanying video from Android Dev Summit for more details on what’s included in the new Material API and how to use it.

To get started, you must include the library as a dependency in your project. As of now, the latest version is 1.0.0-alpha02:

groovy
implementation "androidx.compose.material3:material3:1.0.0-alpha02"

You're now ready to start coding! Since the dynamic colors are only provided by the system on Android 12 and above, you need to check your device's SDK version.

You can use the dynamicLightColorScheme and dynamicDarkColorScheme methods to get a ColorScheme instance.

kt
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    val lightColors: ColorScheme = dynamicLightColorScheme(context)
    val darkColors: ColorScheme = dynamicDarkColorScheme(context)
}

This object includes all the colors described on the user-generated color page of the M3 documentation:

kt
@Stable
class ColorScheme(
    primary: Color,
    onPrimary: Color,
    primaryContainer: Color,
    onPrimaryContainer: Color,
    inversePrimary: Color,
    secondary: Color,
    onSecondary: Color,
    secondaryContainer: Color,
    onSecondaryContainer: Color,
    ...,

If you're using the androidx.compose.material3.MaterialTheme in your app, you can pass in a ColorScheme directly, and the colors in it will be applied to all material3 components:

kt
MaterialTheme(colorScheme = lightColors) {
    Button(...) {
        Text(...)    
    }
}

Be sure to import components such as Button or Text from the androidx.compose.material3 package, and not from androidx.compose.material!

However, you can also use the values in ColorScheme directly in your Jetpack Compose code, which is what you'll do in the next section as you apply them to the Stream Chat SDK.

Theming With Material You Colors

In our Compose Chat SDK, theming is done by using the ChatTheme component. This accepts a StreamColor parameter, and all Chat UI Components use these colors in their implementation.

By default, we ship a set of light and dark colors which are applied automatically based on the system settings for dark mode.

kt
@Composable
public fun ChatTheme(
    isInDarkMode: Boolean = isSystemInDarkTheme(),
    colors: StreamColors = if (isInDarkMode) StreamColors.defaultDarkColors() 
                           else StreamColors.defaultColors(),
    ...
    content: @Composable () -> Unit,
) {
    CompositionLocalProvider(
        LocalColors provides colors,
        ...
    ) {
        content()
    }
}

Check out Customizing the Compose Chat SDK With ChatTheme to learn how you can customize Compose UI components to your requirements.

The Material 3 library exposes a ColorScheme object. To apply those colors to the Chat Components, we need to somehow bridge the values in that object to a StreamColor.

Here's an example of how you could do that:

kt
private fun adapt(scheme: ColorScheme) = StreamColors(
    textHighEmphasis = scheme.onPrimaryContainer,
    textLowEmphasis = scheme.onSecondaryContainer,
    disabled = scheme.inversePrimary,
    borders = scheme.outline,
    inputBackground = scheme.surfaceVariant,
    appBackground = scheme.background,
    barsBackground = scheme.secondaryContainer,
    linkBackground = scheme.primaryContainer,
    overlay = scheme.surface.copy(alpha = 0.5f),
    overlayDark = scheme.inverseSurface.copy(alpha = 0.5f),
    primaryAccent = scheme.primary,
    errorAccent = scheme.error,
    infoAccent = scheme.secondary,
    highlight = scheme.inversePrimary,

This is just a quick example solution based on trial-and-error experimentation. Feel free to tinker around with the code to make the mapping even better!

To take light and dark mode into account, as well as the availability of dynamic colors on the device, you'd also want to perform the following steps:

kt
@Composable
fun createStreamColors(): StreamColors {
    val isInDarkMode = isSystemInDarkTheme()
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        // Use dynamic colors if available
        val scheme = when {
            isInDarkMode -> dynamicDarkColorScheme(LocalContext.current)
            else -> dynamicLightColorScheme(LocalContext.current)
        }
        return adapt(scheme)
    } else {
        // Fall back to the default colors otherwise
        when {
            isInDarkMode -> StreamColors.defaultDarkColors()
            else -> StreamColors.defaultColors()
        }
    }
}

Note: The function above is a @Composable function, which allows it to access isSystemInDarkTheme() dynamically, as well as the LocalContext.

As the final step, you need to pass the colors created here to the ChatTheme that wraps your Chat Components to apply the customizations:

kt
setContent {
    ChatTheme(colors = createStreamColors()) {
        MessagesScreen(
            channelId = channelId,
            onBackPressed = this::finish
        )
    }
}

If you run your app on an Android 12 device now, you'll see various color schemes based on the colors that the system extracts from your current wallpaper!

You can also toggle between light and dark mode, and the colors will update automatically:

You can check out the implementation above in action by grabbing the code from the GitHub repository for this tutorial and running it on your own device.

Theming With Material Theme Builder

The Material Design team introduced Material Theme Builder, which helps you visualize Material You's dynamic color and create a custom Material Design 3 theme.

The Material Theme Builder on the Web lets you build dynamic color schemes on the web client.

One of the noticeable features of the Material Theme Builder is that you can export your custom themes as native codes such as Android Views (XML) and Jetpack Compose (includes Theme, Color, and Type). As you can see below, Jetpack Compose codes based on a custom theme were generated on the website.

kt
val md_theme_light_primary = Color(0xFF6750A4)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFEADDFF)
val md_theme_light_onPrimaryContainer = Color(0xFF21005D)
...

val md_theme_dark_primary = Color(0xFFD0BCFF)
val md_theme_dark_onPrimary = Color(0xFF381E72)
val md_theme_dark_primaryContainer = Color(0xFF4F378B)
val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF)
...

private val LightThemeColors = lightColorScheme(

    primary = md_theme_light_primary,

The end result with the generated theme will look like this:

Wrapping Up

In this tutorial, you saw how easy it is to pull colors from the Material 3 Compose library and use them in our Chat SDK for Compose. Feel free to keep iterating on the adaptation code to achieve even nicer results!

To learn more about the Compose Chat SDK, try the Compose Chat Tutorial or check out the Compose Chat documentation that covers all the available components, how to use them, and what customization options are available.

As always, keep coding!