Table of contents
The material theme API in Jetpack compose comes furnished with a set of predefined colors, typography, themes etc.
On many occasions, we may want to create custom themes to use throughout our compose UI.
Let's get started.
Layout
In our root directory, you will come across this folder quite often:
Here, you have predefined Kotlin files that contain the colors, material themes and typography respectively.
It is quite common to call the color schemes stored in our theme file as shown below:
@Composable
fun MyBox() {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(16.dp)
) {
Text(text = "Hello there")
}
}
Here, we call MaterialTheme.colorScheme.background
to get a background color defined in the MaterialTheme class.
However, looking at the padding modifier, we are passing a hard-coded value i.e 16.dp
. Not only is this not a good practice, but it also makes our code hard to maintain. Hence, we need a way to go around this.
Let us now add an extra file that will hold for us a custom class that contains spacing which we can use throughout our UI instead of using the hard-coded values everywhere.
Spacing.kt
Let us define a new package inside the ui
package called custom
that will contain all our custom themes.
Inside our Spacing.kt
file, let's define a data class that will hold the values of our custom spacing options.
data class Spacing(
val default: Dp = 0.dp,
val extraSmall: Dp = 4.dp,
val small: Dp = 8.dp,
val medium: Dp = 16.dp,
val large: Dp = 24.dp,
val extraLarge: Dp = 32.dp
)
Here, our Spacing
data class contains values that we can use in our UI to specify the amount of spacing in Dp
that we require.
Next, we need to create an instance of our class that compose can recognize as part of the MaterialTheme Local property by doing it as below.
val LocalSpacing = compositionLocalOf { Spacing() }
However, this is not all as we need to explicitly define the LocalSpacing
variable inside the MaterialTheme composable.
Inside our Theme.kt
file, we do the following inside our MyTheme composable.
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
// ...
// we enclose MaterialTheme composable within CompositionLocalProvider
CompositionLocalProvider(
LocalSpacing provides Spacing()
) {
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
}
Here, we enclose the MaterialTheme composable within the CompositionLocalProvider
composable function to provide an instance of our Spacing
class that MaterialTheme can recognize.
Wouldn't it be cool if we could call our spacing values the way we would call values specified in the MaterialTheme class? eg. MaterialTheme.spacing.medium
Well, we can extend the MaterialTheme class inside our Spacing.kt
file to get access to this functionality.
val MaterialTheme.spacing: Spacing
@Composable
@ReadOnlyComposable
get() = LocalSpacing.current
And that's it! Here's how our final Spacing.kt
file looks:
data class Spacing(
val default: Dp = 0.dp,
val extraSmall: Dp = 4.dp,
val small: Dp = 8.dp,
val medium: Dp = 16.dp,
val large: Dp = 24.dp,
val extraLarge: Dp = 32.dp
)
val LocalSpacing = compositionLocalOf { Spacing() }
val MaterialTheme.spacing: Spacing
@Composable
@ReadOnlyComposable
get() = LocalSpacing.current
Looking back at our initial composable, MyBox
composable, we can now modify our padding to use the defined values in our Spacing class as shown below:
@Composable
fun MyBox() {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.padding(MaterialTheme.spacing.medium)
) {
Text(text = "Hello there")
}
}
Hurray! We have successfully gotten rid of the hardcoded values and now our code is all clean.
GitHub Link
You can view the full project on this GitHub Repository
Thank you and see you soon.