10

How to create a truly custom theme in Jetpack Compose 🎨

 2 years ago
source link: https://proandroiddev.com/how-to-create-a-truly-custom-theme-in-jetpack-compose-55fb4cd6d655
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to create a truly custom theme in Jetpack Compose 🎨

Jetpack Compose comes with the Material theme by default. It does a great job of styling apps that already follow Material guidelines. Moreover, Google published a comprehensive guide on how to customize it.

As it’s often the case, most apps only partially follow Material guidelines and have their own custom design systems. In this situation, MaterialTheme cannot be used as it provides only Material styling.

In this article, we will learn how to create a truly custom theme that follows our own design system.

How to create a custom theme

We will implement a custom theme in a similar manner as MaterialTheme. It will have a different set of attributes and their properties.
But first, let’s answer a few important questions before diving into the code:

  • Can I use XML styles in Jetpack Compose?
  • How does MaterialTheme work?

XML styles in Jetpack Compose

Unfortunately, XML styles are mostly not compatible with Jetpack Compose.

It’s impossible to reuse existing text styles or themes in JC layouts besides retrieving basic resources such as colors, dimensions, etc.
However, I would also highly recommend recreating these in Jetpack Compose, which is covered in this article as well.

How Material Theme works

If you take a look at the MaterialTheme implementation you will notice that it consists of 2 main parts:

  1. The MaterialTheme object that contains attributes (colors, typography, shapes)

2. The Composable that wraps UI and passes styling attributes down to inner composables.

In the following sections, we will create our own theme in a very similar way, but it will contain:

  • colors
  • typography
  • dimensions

Create AppTheme

First, create the AppTheme object class that will contain colors, typography and dimension attributes holders. Each of these components are going to be implemented in the following sections.

Your theme attributes are accessed with the AppTheme object.

Add app colors

For the sake of simplicity, our demo app will have only 5 colors:

  • primary
  • textPrimary
  • textSecondary
  • background
  • error

We will also add an isLight property to define if the color set is for a light or dark mode.

AppColors will be the most complicated component among the three as it can change its values in runtime(also to support both light and dark modes).

Create the AppColors class. It should contain:

  • specified color tokens
  • getters and setters
  • a copy function

data class cannot be used here because of mutableStateOf which is added to support dynamical color change.

What’s mutableStateOf?
mutableStateOf creates a MutableState object that holds the color value. The value is wrapped into MutableState for Jetpack Compose to observe the writes to this property and then update the UI that uses it.

Setting your colors

Create your colors below the AppColors class as variables:

0xFFFFB400 is just an ARGB color int. Having a color hex(for example, #FFB400) you can turn it into ARGB color int by adding 0xFF in the beggining. To set transparancy to the color, update the FF value after 0x

Add a lightColors() function to create a light theme color scheme for the app:

To support a dark theme just add dark mode colors and create a darkColors() function in the same way as the lightColors():

Accessing the colors

The last step is to create the LocalColors object. It’s an instance of CompositionLocal which does 2 things:

  • implicitly passes color attributes through the composition tree of our UI
  • updates the UI once some value changes

Create it with staticCompositionLocalOf as it will trigger the whole content lambda to be recomposed once some value is changed.

internal val LocalColors = staticCompositionLocalOf{ lightColors() }

Add text styles

Text styles(typography) are added in a very similar way.
Specify the type scales(text types) of your design system. In this demo we will have the following type scales:

  • subtitle
  • button
  • caption

Create AppTypography

Add a data class named AppTypography whose properties are the type scales (h1, subtitle, body, etc.). Assign the default values right in the constructor, setting an appropriate TextStyle for each element.
We will also add some custom fonts to the app. I’ll create them right above the typography class:

Don’t forget about LocalTypography to store and provide the typography.

Add dimensions

Dimensions are added in a similar manner as typography. Set default values to the constructor and add the LocalDimensions holder to access the dimensions.

Make AppTheme Composable

We just created AppColors, AppTypography and AppDimensions for our styling attributes.

Now it’s time for the final touch.

Add the AppTheme composable that will wrap our UI content(just as MaterialTheme does) and pass the current styling to the composables inside.

To do so, make a Composable function named AppTheme with 4 parameters:

  • colors
  • typography
  • dimensions
  • content lambda

The first 3 arguments should use default values from AppTheme object.

To pass the actual values from your styling attributes, you should wrap content lambda with a CompositionLocalProvider. It will tell LocalColors, LocalDimensions and LocalTypography to provide the styling attributes you pass to the theme function to the inner composables.

And that’s it. Now when we created a custom theme in Jetpack Compose, let’s make a screen with it and style the UI inside.

Styling UI elements

In this section, I’ll focus singly on the UI part, but you can have a look at the entire project in the GitHub repo.

For a demo, we will create a very basic app that will show us a list of photographs, their descriptions, and authors.
First, create a screen and wrap it into the AppTheme:

DemoScreen is basically a Composable that holds all UI elements(other composables) of the screen. We will make a simple scrollable list(LazyColumn) with a toolbar.

Now let’s add some styling. First, set the background of the LazyColumn to the background color of our theme. Then style the toolbar title by assigning h1 scale type and primary color to its properties:

As you can see, styling attributes can be easily accessed with AppTheme object. It always holds the actual values as they are provided from a CompositionLocal.

Now let’s have a look at the GalleryItem. We want our list items to look as simple as this:

0*4lShZXBBVjWbSXD2.png?q=20
how-to-create-a-truly-custom-theme-in-jetpack-compose-55fb4cd6d655

So the GalleryItem composable will have:

  • description text of the content
  • the photograph itself
  • a caption with the name of the author

To achieve this, we create a Column and put Text, Image and Text composables inside for the description, photograph and caption accordingly. Then apply the typography and colors from AppTheme and you will get:

Build the app to make sure that the screen is using the styling we applied:

0*VKtyXq-LyOFs7QaR.png?q=20
how-to-create-a-truly-custom-theme-in-jetpack-compose-55fb4cd6d655

Applying dark mode

To apply the dark theme just pass darkColors() to your theme:

AppTheme(colors = darkColors()) {
DemoScreen(items = photographItems)
}

You can also check if the system theme is dark and then retrieve a proper set of colors:

val darkTheme: Boolean = isSystemInDarkTheme()
val colors = if (darkTheme) darkColors() else lightColors()

As the result, all composables inside your theme will have updated styling attributes:

0*xuVBlMc_RcrpuW4i.png?q=20
how-to-create-a-truly-custom-theme-in-jetpack-compose-55fb4cd6d655

That’s all! Thank you for reading!

Further reads

Full code in the GitHub repo
Theming in Compose
Material Theme source code
Jetpack Compose theming codelab

My engineering blog 📝
My twitter 💬


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK