A Compose Multiplatform library for generating color palettes from images, including the dominant color. You can use this library in combination with MaterialKolor to generate dynamic Material themes based on images.
Supports loading images from several sources, see sources.
Note: This is a port of
the androidx.palette
library.
- Platforms
- Inspiration
- Dynamic Material Themes
- Setup
- Usage
- Migration
- Feature Requests
- Contributing
- License
This library is written for Compose Multiplatform and can be used on the following platforms:
| Artifact | Android | Desktop | iOS | macOS | JS | WASM |
|---|---|---|---|---|---|---|
kmpalette-core |
β | β | β | β | β | β |
extensions-base64 |
β | β | β | β | β | β |
extensions-network |
β | β | β | β | β | β |
extensions-file |
β | β | β | β | β | β |
I created this library because I wanted to use the
androidx.palette library in a
Compose Multiplatform app. But that library is not multiplatform, so I decided to port it.
Want to create a dynamic Material theme based on the dominant color in an image?
Check out my other Compose Multiplatform library MaterialKolor!
You can add this library to your project using Gradle. There are several optional extension libraries, see sources.
In libs.versions.toml:
[versions]
kmpalette = "4.0.0"
[libraries]
kmpalette-core = { module = "com.kmpalette:kmpalette-core", version.ref = "kmpalette" }
# Optional source libraries
kmpalette-extensions-base64 = { module = "com.kmpalette:extensions-base64", version.ref = "kmpalette" }
kmpalette-extensions-network = { module = "com.kmpalette:extensions-network", version.ref = "kmpalette" }
kmpalette-extensions-file = { module = "com.kmpalette:extensions-file", version.ref = "kmpalette" }To add to a multiplatform project, add the dependency to the common source-set:
kotlin {
sourceSets {
commonMain {
dependencies {
// Core library
implementation(libs.kmpalette.core)
// Optional extensions based on your image source
implementation(libs.kmpalette.extensions.base64)
implementation(libs.kmpalette.extensions.network)
implementation(libs.kmpalette.extensions.file)
}
}
}
}To see the generated KDocs, visit docs.kmpalette.com
To use this library, you first must have a ImageBitmap or a Painter object.
To get an ImageBitmap you can use one of the sources or by using a library that
creates one for you.
Since this library is a port of
the androidx.palette library,
the usage is very similar. However, this library provides some helpful extension functions and
composables.
Look in kmpalette-core for the main library, including extensions for the Palette and Swatch
objects.
Included are two helpful @Composable-ready State objects:
DominantColorState- A state object that holds a generated dominantColorobject.PaletteState- A state object that holds a generatedPaletteobject.
They can be used like so:
You can generate a dominant color from an ImageBitmap using the rememberDominantColorState
composable. This will also provide a onColor for you to use as a text color.
@Composable
fun SomeComposable(bitmap: ImageBitmap) {
val dominantColorState = rememberDominantColorState()
LaunchedEffect(bitmap) {
dominantColorState.updateFrom(bitmap)
}
Box(
modifier = Modifier
.width(200.dp)
.height(100.dp)
.background(dominantColorState.color)
) {
Text("Some Text", color = dominantColorState.onColor)
}
}You can also use a Painter object with the dedicated rememberPainterDominantColorState:
@Composable
fun SomeComposable(painter: Painter) {
val dominantColorState = rememberPainterDominantColorState()
LaunchedEffect(painter) {
dominantColorState.updateFrom(painter)
}
// ...
}Since the generation of the dominant color is an asynchronous operation that can fail, you can track
the results of the operation using the DominantColorState.result object.
If you want to filter the dominant color, you can use the pass a lambda
to rememberDominantColorState():
val dominantColorState = rememberDominantColorState(
isSwatchValid = { swatch ->
swatch.color.contrastAgainst(MaterialTheme.colorScheme.surfaceColor) >= MinContrastRatio
}
)
LaunchedEffect(bitmap) {
dominantColorState.updateFrom(bitmap)
}If you want a whole color palette instead of just a dominant color, you can use
the rememberPaletteState composable. This will provide a Palette object which contains a few
different color Swatchs, each has their own color and onColor.
Using an ImageBitmap:
@Composable
fun SomeComposable(bitmap: ImageBitmap) {
val paletteState = rememberPaletteState()
LaunchedEffect(bitmap) {
paletteState.generate(bitmap)
}
Box(
modifier = Modifier
.width(200.dp)
.height(100.dp)
.background(paletteState.vibrantSwatch?.color ?: Color.White)
) {
Text(
text = "Some Text",
color = paletteState.vibrantSwatch?.onColor ?: LocalContentColor.current,
)
}
}Or using a Painter:
@Composable
fun SomeComposable(painter: Painter) {
val paletteState = rememberPainterPaletteState()
LaunchedEffect(painter) {
paletteState.generate(painter)
}
// ...
}Since the generation of the palette is an asynchronous operation that can fail, you can track
the results of the operation using the PaletteState.state object.
The kmpalette-core library provides the core functionality for generating color palettes from
a ImageBitmap or a Painter object.
This library provides some extension artifacts for popular sources.
| Artifact | Library | Loader | Input Class |
|---|---|---|---|
extensions-base64 |
N/A | Base64Loader |
String |
extensions-network |
Ktor | NetworkLoader |
Url |
extensions-file |
FileKit | PlatformFileLoader |
PlatformFile |
Each of these extensions provides dedicated composable functions. For example,
the extensions-network module provides rememberNetworkDominantColorState and
rememberNetworkPaletteState:
@Composable
fun SomeComposable(url: Url) {
val dominantColorState = rememberNetworkDominantColorState(
defaultColor = MaterialTheme.colorScheme.primary,
defaultOnColor = MaterialTheme.colorScheme.onPrimary,
)
LaunchedEffect(url) {
dominantColorState.updateFrom(url)
}
Box(
modifier = Modifier
.width(200.dp)
.height(100.dp)
.background(dominantColorState.color)
) {
Text("Some Text", color = dominantColorState.onColor)
}
}To generate a palette from a DrawableResource you can use the rememberResourceDominantColorState
or rememberResourcePaletteState composables which work directly with Compose Multiplatform
resources:
@Composable
fun MyComposable() {
val dominantColorState = rememberResourceDominantColorState()
LaunchedEffect(Unit) {
dominantColorState.updateFrom(Res.drawable.my_image)
}
}Alternatively, you can use the @Composable imageResource() to get an ImageBitmap then pass that
to the default loader:
@Composable
fun MyComposable() {
val image = imageResource(Res.drawable.my_image)
val dominantColorState = rememberDominantColorState()
LaunchedEffect(image) {
dominantColorState.updateFrom(image)
}
}If you are migrating from version 3.x, please see the Migration Guide for detailed instructions on updating your code.
Key changes in 4.0:
extensions-bytearraymodule has been removedextensions-filenow uses FileKit instead of Okio- New dedicated composable functions for each loader type
If you have a feature request, please open an issue. If you would like to implement a feature request, refer to the Contributing section.
Contributions are always welcome!. If you'd like to contribute, please feel free to create a PR or open an issue.
The module androidx-palette is licensed under the Apache License, Version 2.0. See
their LICENSE and their
repository here for more
information.
- Convert Java code to Kotlin
- Convert library to Kotlin Multiplatform
For the remaining code see LICENSE for more information.