Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add extra colors into MaterialTheme in Android Jetpack Compose?

Android Jetpack Compose Colors class contains a set of color types a material themed app can be implemented with. In case my app theme requires some extra color types, how can I add these extra colors so they would be available via MaterialTheme object?

like image 285
Valeriy Katkov Avatar asked Feb 01 '21 14:02

Valeriy Katkov


3 Answers

There are basically two approaches to this:

The naive way is to just create extension properties tot hard-code the colors. If you don't want to support multiple themes or light/dark mode this works just fine.

If you want to integrate your colors properly to the theme, you can read my article on it, as it's a bit long to inline here: https://gustav-karlsson.medium.com/extending-the-jetpack-compose-material-theme-with-more-colors-e1b849390d50

But in short, the steps are:

  • Create your own MyColors class that holds a reference to Colors as well as your new colors.
  • Create a CompositionLocal that holds a MyColor instance.
  • Create your theme and wrap the MaterialTheme in a CompositionLocalProvider where the CompositionLocal provides MyColors. Make sure to also assign the Colors instance from MyColors to the MaterialTheme.
  • Create an extension property on MaterialTheme that refers to the CompositionLocal holding MyColors. This is how you refer to your new colors.

This will allow you to introduce new colors to the theme that will update dynamically as the theme changes.

like image 192
Gustav Karlsson Avatar answered Nov 11 '22 02:11

Gustav Karlsson


Just a small change to Valeriy Katkov's answer

In some versions of the android studio, the following code will not work

@Composable
val Colors.myExtraColor: Color
    get() = if (isLight) Color.Red else Color.Green

@Composable
fun ExtraColorExample() {
    Text(
        text = "test",
        color = MaterialTheme.colors.myExtraColor // <-- the newly added color
    )
}

It will show an error

This annotation is not applicable to target 'top-level property without backing field or delegate'

To fix this either write it like this

@get:Composable
val Colors.myExtraColor: Color
    get() = if (isLight) Color.Red else Color.Green

Or

val Colors.myExtraColor: Color
    @Composable
    get() = if (isLight) Color.Red else Color.Green

The version I found this error in

Android Studio Arctic Fox | 2020.3.1 Canary 12
Build #AI-203.7148.57.2031.7226969, built on March 23, 2021
Runtime version: 11.0.8+10-b944.6842174 amd64
VM: OpenJDK 64-Bit Server VM by N/A
Windows 10 10.0
like image 32
AgentP Avatar answered Nov 11 '22 00:11

AgentP


Extending Colors class

You can easily add an extension property to the Colors class so it would be available via any Colors object across your app.

@Composable
val Colors.myExtraColor: Color
    get() = if (isLight) Color.Red else Color.Green

@Composable
fun ExtraColorExample() {
    Text(
        text = "test",
        color = MaterialTheme.colors.myExtraColor // <-- the newly added color
    )
}

There's an example in the compose documentation as well, see Extending Material colors.

Specifying a content alpha

In case the color you're missing differs from an existing one only by it's alpha and the purpose of the color is to change a content priority, there's no need to add an additional color to the theme. You can specify a content alpha for a hierarchy by providing a value for LocalContentAlpha.

CompositionLocalProvider(
    LocalContentAlpha provides ContentAlpha.medium,
    LocalContentColor provides MaterialTheme.colors.onSurface
) {
    // this text is displayed using ContentAlpha.medium
    // and MaterialTheme.colors.onSurface color
    Text("Hello world!") 
}

See Content Alpha documentation for more details. There's also Content Alpha section in Jetpack Compose Theming codelab.

like image 8
Valeriy Katkov Avatar answered Nov 11 '22 00:11

Valeriy Katkov