Published in


Customizing Material Theme with Jetpack Compose

When Jetpack Compose went stable in July 2021, many Android developers started experimenting with it to see if it’s mature enough to be used in production. Spoiler alert: it’s more than ready and more delightful than XML!

At the same time, we decided to redesign our mobile app called Codeforces WatchR, which is available on both iOS and Android. So it was a no-brainer to execute this redesign for the Android app using Jetpack Compose.

Redesigned Codeforces WatchR app in the dark mode

This article will describe the first step in our Jetpack Compose journey — defining and using the Theme. Jetpack Compose theme, to be exact, since, as it’s turned out, XML themes aren’t fully compatible with Jetpack Compose.

@Composable Theme

As you already know, Jetpack Compose is built on the concept of Composable functions. It means that our whole UI is declared as a hierarchy of functions capable of rendering all possible views for all possible states.

The theme doesn’t make an exception to this rule. If we think about it, colors and fonts can easily change while the application is on the screen. Hence we need to recompose our UI to reflect the change (light/dark mode change, for example). So it makes total sense to have a theme as a Composable function.

Even though there is a library that provides partial compatibility between XML and Jetpack Compose themes, the conversion is far from perfect. So we decided to support two sets of themes for the transition period.

The AppCompat Compose Theme Adapter library allows you to easily re-use AppCompat XML themes for theming in Jetpack Compose. It creates a MaterialTheme with the color and typography values from the context's theme.

Theme Structure

As discussed above, the theme is a Composable function. It takes the content to be themed and returns … nothing. But it provides an adequately configured theme object to the content by using CompositionLocalProvider.

In the Theme object, we get a number of systems. There are color, typography, and shape systems in the basic version. But we can add custom systems to the Theme object if needed. All those systems are simple data classes, by the way.

Theme structure from Android Developers

Then we can access those systems and their values in our Composable components as simply as Theme.ColorSystem.color. If you think it resembles singleton, you are right!

Theme Customization

As soon as we need to add any Composable to our UI, it’s easy to see that they don’t come bundled with Jetpack Compose. Instead, we need to import androidx.compose.material library.

It gives us a hint about how those components are styled. MaterialTheme is the correct answer! There is no magic about how this default theme is working. It contains three systems — colors, typography, and shapes.

Material components use values from those systems as default parameters. So to customize the look and feel of our UI, we need to override those values. You can find our first attempt below.

And here, you can find the package with a totality of classes we needed to customize the default material theme to our needs.

Customization Limits

Material systems are final classes, so we can’t inherit them. Nonetheless, there are multiple options if we need more flexibility from the theme:

Depending on the customization needs, we may choose different options.


So far, we’ve customized the default MaterialTheme. But taking into account the complexity of our new design and its remoteness from the classical material guidelines, we consider implementing a fully-custom design system.

This will require the complete customization of all material components because the default values won’t be appropriately provided anymore. But the flexibility given by this option may be worth the investment.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store