jordond / MaterialKolor

🎨 A Compose multiplatform library for generating dynamic Material3 color schemes from a seed color
https://materialkolor.com
MIT License
519 stars 10 forks source link
android compose compose-multiplatform compose-web kotlin kotlin-multiplatform material material-design material3
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:

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

For the remaining code see LICENSE for more information.