/MaterialKolor

🎨 A Compose multiplatform library for generating dynamic Material3 color schemes from a seed color

Primary LanguageKotlinMIT LicenseMIT

logo


Maven Central Kotlin Build License

Compose Multiplatform badge-android badge-ios badge-desktop badge-js

A Compose Multiplatform library for creating dynamic Material Design 3 color palettes from any color.

Check out MaterialKolor Builder to see MaterialKolor in action and generate your own color schemes. It can export to MaterialKolor code, or plain Material 3 code.

The KDoc is published at docs.materialkolor.com

Table of Contents

Platforms

This library is written for Compose Multiplatform, and can be used on the following platforms:

  • Android
  • iOS
  • JVM (Desktop)
  • JavaScript/wasm (Browser)

You can see it in action by using MaterialKolor Builder.

Inspiration

The heart of this library comes from the material-color-utilities repository. It is currently only a Java library, and I wanted to make it available to Kotlin Multiplatform projects. The source code was taken and converted into a Kotlin Multiplatform library.

I also incorporated the Compose ideas from another open source library m3color.

Setup

You can add this library to your project using Gradle.

Multiplatform

To add to a multiplatform project, add the dependency to the common source-set:

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation("com.materialkolor:material-kolor:2.0.0")
            }
        }
    }
}

Single Platform

For an Android only project, add the dependency to app level build.gradle.kts:

dependencies {
    implementation("com.materialkolor:material-kolor:2.0.0")
}

Version Catalog

[versions]
materialKolor = "2.0.0"

[libraries]
materialKolor = { module = "com.materialkolor:material-kolor", version.ref = "materialKolor" }

Usage

To generate a custom ColorScheme you simply need to call dynamicColorScheme() with your target seed color:

@Composable
fun MyTheme(
    seedColor: Color,
    useDarkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = rememberDynamicColorScheme(seedColor, useDarkTheme)

    MaterialTheme(
        colors = colorScheme.toMaterialColors(),
        content = content
    )
}

You can also pass in a PaletteStyle to customize the generated palette:

dynamicColorScheme(
    seedColor = seedColor,
    isDark = useDarkTheme,
    style = PaletteStyle.Vibrant,
)

DynamicMaterialTheme

A DynamicMaterialTheme Composable is also available. It is a wrapper around MaterialTheme that uses dynamicColorScheme() to generate a ColorScheme for you. You can animate the color scheme by passing in animate = true.

Example:

@Composable
fun MyTheme(
    seedColor: Color,
    useDarkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    DynamicMaterialTheme(
        seedColor = seedColor,
        isDark = useDarkTheme,
        animate = true,
        content = content
    )
}

Extensions

Included in the library are some extensions for working with colors. You can check out the /ktx package for more information.

But here are a couple useful examples:

Harmonize Colors

If you want to harmonize a color with another you can use the Color.harmonize() function. You can read more about color harmonization on the Material 3 Documentation.

Example:

val newColor = MaterialTheme.colorScheme.primary.harmonize(Color.Blue)

There is an additional function specifically for harmonizing with the primary color:

val newColor = Color.Blue.harmonizeWithPrimary()

Note: Color.harmonize() has an optional parameter matchSaturation which when set to true will adjust the saturation from the other color.

Lighten and Darken

You can lighten or darken a color using the Color.lighten() and Color.darken() functions.

For example:

val newColor = MaterialTheme.colorScheme.primary.lighten(0.2f)

Check out the demo app for a full example.

Color Temperature

You can determine if a Color is warm or cold using the following:

val isWarm = MaterialTheme.colorScheme.primary.isWarm()
val isCold = MaterialTheme.colorScheme.primary.isCold()

Generating from an Image

You can calculate a seed color, or colors that are suitable for UI theming from an image. This is useful for generating a color scheme from a user's profile picture, or a background image.

To do so you can call ImageBitmap.themeColors(), ImageBitmap.themeColor() or the @Composable function rememberThemeColors() or rememberThemeColor():

fun calculateSeedColor(bitmap: ImageBitmap): Color {
    val suitableColors = bitmap.themeColors(fallback = Color.Blue)
    return suitableColors.first()
}

See ImageBitmap.kt for more information.

Or in Compose land:

@Composable
fun DynamicTheme(image: ImageBitmap, content: @Composable () -> Unit) {
    val seedColor = rememberThemeColor(image, fallback = MaterialTheme.colorScheme.primary)

    AnimatedDynamicMaterialTheme(
        seedColor = seedColor,
        content = content
    )
}

Advanced

For a more advanced use-case you can use my other library kmPalette.

You can get the dominant color from an image, or you can also generate a color palette.

Follow the instructions there to set it up, then as an example. You can use it to generate a color theme from a remote image:

@Composable
fun SampleTheme(
    imageUrl: Url, // Url("http://example.com/image.jpg")
    useDarkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    val networkLoader = rememberNetworkLoader()
    val dominantColorState = rememberDominantColorState(loader = networkLoader)
    LaunchedEffect(imageUrl) {
        dominantColorState.updateFrom(imageUrl)
    }

    AnimatedDynamicMaterialTheme(
        seedColor = dominantColorState.color,
        isDark = useDarkTheme,
        content = content
    )
}

License

The module material-color-utilities is licensed under the Apache License, Version 2.0. See their LICENSE and their repository here for more information.

Changes from original source

  • Convert Java code to Kotlin
  • Convert library to Kotlin Multiplatform

For the remaining code see LICENSE for more information.