🚀 All about theming in Jetpack Compose
Heya! Happy Thursday. Alex here.
Today's tutorial is going to cover how to use themes in your Jetpack Compose app, using Material 3 compose.
You can still use this tutorial with Material compose. What's different is that they use different theme attributes (such as colors
instead of colorScheme
) and different imports, but the main concept is the same.
For a tl;dr what's different between Material and Material 3, checkout this Twitter thread.
Thinking in Themes
It all starts with the MaterialTheme()
composable. This composable is responsible for setting up the look and feel for all the content of its children.
Most of the times, it is going to be the top level composable of your app:
MaterialTheme {
HomeScreen()
}
out of the box, the MaterialTheme()
composable gives you 3 different attributes which you can pass as parameters:
@Composable
fun MaterialTheme(
colorScheme: ColorScheme = MaterialTheme.colorScheme,
shapes: Shapes = MaterialTheme.shapes,
typography: Typography = MaterialTheme.typography,
content: @Composable () -> Unit
)
The parameters that you pass here are going to be used in any child composable passed to the content.
Example of this are ModalBottomSheets
that were recently introduced in Material 3 compose. The value of the extraLarge parameter of the passing Shapes
object will affect how the bottom sheet is going to look like. More on that in a bit.
The cool thing is MaterialTheme()
's children composables will have access to any parameter passed here via the MaterialTheme
object.
At any point that you need to access, say, the specified Typography
, you can use the MaterialTheme
object.
Here is a quick example:
MaterialTheme(
typography = Typography(
headlineLarge = TextStyle(
fontSize = 24.sp
)
)
) {
HomeScreen()
}
and here is how to access the headlineLarge
property from any child:
@Composable
fun HomeScreen() {
Text("This is my title", style = MaterialTheme.typography.headlineLarge)
}
you can use the MaterialTheme
object to get any color, type style or shape you need, as long as the composable is wrapped with the MaterialTheme
composable.
In other words: the MaterialTheme()
composable gives access to the MaterialTheme
object to all of its children and its children children.
By default, most (if not all) composables that come out of the box with Material 3 will use this object for default values. This means that updating the values passed to your theme will update the look and feel of your app.
Every MaterialTheme
property explained
The 3 different attributes you can modify in your MaterialTheme (colorScheme, shapes, typography) are an implementation of how the Material 3 design system works.
Keep reading to learn how each attribute works and how it affects the looks of your app:
What is colorsScheme
and how it works in Material 3 compose
Material 3 comes with a list of predefined (named) colors. The main ones are: primary, surface and background.
Primary is the main highlight of your app (for things like button backgrounds and other interactive elements). Surface sets the color for surfaces such as Cards and Bottom Sheets (a reminder that a surface is a piece of paper in the Material metaphor). Background is, well, the background of your app.
Every color comes with its pair. For each color in the colorScheme
you will find a on_
counterpart. For example primary
comes with onPrimary
. The onPrimary
is used for elements that are placed on top of other elements using the primary
color.
A good example of this are buttons. They use primary
as their background color. Their text uses the onPrimary
color.
This works for any color in Material. Need to change the color of text that is used on top of a card? Update the onSurface
color and the text will magically update to the given onSurface
color.
Do not create ColorScheme()
via its constructor
Instead of defining a ColorScheme
from scratch (which requires a ton of constructor arguments) use one of the color scheme functions instead. Those are used as a base to definine your own themes. Those include lightScheme()
(a base for light themes), darkScheme()
(a base for dark themes).
dynamicLightColorScheme()
and dynamicDarkColorScheme()
are also available. These color schemes will create dynamic colors according to the user's wallpaper.
What is typography
and how it works in Material 3 compose
Typography defines the styling of text in your app. All text styles are split in display, headline, title, body, and label. Each category comes in different size variations: small, medium and large.
Each combination is a TextStyle
. Each TextStyle
can contain anything related to the looks of text. This include the text fontSize
, fontWeight
, fontFamily
, lineHeight
to name a few.
The type of TextStyle
your Text()
composable is going to receive depends on the composable that it is wrapped in. For example, the TopAppBar()
title uses the titleLarge
textStyle.
What is shapes
and how it works in Material 3 compose
Shape defines the looks (mostly corners) of different elements in your app. This affects composables such as buttons shapes, card and sheets.
Jetpack Compose comes with some predefined shapes you can use depending on your needs such as CircleShape
and RectangleShape
.
If you need rounded corners, checkout the RoundedCornerShape()
function. CutCornerShape()
is also available.
How to tell how a composable will be visually affected if I change my theme?
Check the default values of the composable you are interested in. Let's find out which theme value will change the background color of our Button()
s.
Notice the colors
default value of the Button()
composable:
fun Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
colors: ButtonColors = ButtonDefaults.buttonColors(),
// ... other parameters
)
The ButtonDefaults.buttonColors()
function is the place where the Button()
receives all of its colors.
If you check the source code of the Button()
you will The background color of the button is called containerColor
fun buttonColors(
containerColor: Color = FilledButtonTokens.ContainerColor.toColor(),
// other parameters
)
If you check the ContainerColor
you will notice its value is ColorSchemeKeyTokens.Primary
. Digging deeper you will find this value is mapped to MaterialTheme.colorScheme.primary
color.
Figuring out which value is affected is a bit tricky. You wouldn't need to do this often though as the composables that come out of the box should look and consistent according to your theme.
In this tutorial you learnt how to theme your app using Material 3 compose library. You learnt about the MaterialTheme()
composable and how it grants access to the MaterialTheme
object to all of its children (and their children). You learnt about the different attributes you can customize: colorScheme, typography and shapes.
⚡️ Breaking Jetpack Compose news
Jetpack Compose 1.4.0 was released yesterday along with a few others!
Here are all the new Jetpack Compose updates we got:
- 🚀 Compose UI 1.4.0 (changelog)
- 🚀 Compose UI 1.5.0-alpha01 (changelog)
- 🎬 Compose Animation 1.4.0 (changelog)
- 🎬 Compose Animation 1.5.0-alpha01 (changelog)
- 🧰 Compose Compiler 1.4.4 (changelog)
- 🧱 Compose Foundation 1.4.0 (changelog)
- 🧱 Compose Foundation 1.5.0-alpha01 (changelog)
- 🎨 Compose Material 1.4.0 (changelog)
- 🎨 Compose Material 1.5.0-alpha01 (changelog)
- 🎨 Compose Material 3 1.1.0-beta01 (changelog)
- 🏃♂️ Compose Runtime 1.4.0 (changelog)
- 🏃♂️ Compose Runtime 1.5.0-alpha01 (changelog)
- ⌚️ Wear Compose 1.2.0-alpha07 (changelog)
- ⌚️🎨 Wear Compose Material3 material3-1.0.0-alpha01 (changelog)
That's all I got for you today.
How did you find this issue? Let me know by replying to this email if you are reading in your inbox.
Until the next one,
Alex