
Generates color palettes from images, including the dominant color, and integrates with Material themes. Supports various image sources and provides extensions and composables for easy implementation.
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.
This library is written for Compose Multiplatform and can be used on the following platforms:
| Artifact | Android | Desktop | iOS | macOS | JS | WASM |
|---|---|---|---|---|---|---|
androidx-palette |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
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.materialkolor.palette:core", version.ref = "kmpalette" }
# Optional - standalone palette generation without Compose
kmpalette-androidx-palette = { module = "com.materialkolor.palette:androidx-palette", version.ref = "kmpalette" }
# Optional source libraries
kmpalette-extensions-base64 = { module = "com.materialkolor.palette:extensions-base64", version.ref = "kmpalette" }
kmpalette-extensions-network = { module = "com.materialkolor.palette:extensions-network", version.ref = "kmpalette" }
kmpalette-extensions-file = { module = "com.materialkolor.palette: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 (includes Compose utilities)
implementation(libs.kmpalette.core)
// Or use just the palette generation without Compose
// implementation(libs.kmpalette.androidx.palette)
// Optional extensions based on your image source
implementation(libs.kmpalette.extensions.base64)
implementation(libs.kmpalette.extensions.network)
implementation(libs.kmpalette.extensions.file)
}
}
}
}The androidx-palette module is a full Kotlin Multiplatform port of
the androidx.palette library. It
has no dependency on Compose and can be used in any Kotlin Multiplatform project.
If you don't need the Compose utilities provided by core, you can depend on androidx-palette
directly to generate color palettes from raw pixel data:
kotlin {
sourceSets {
commonMain {
dependencies {
implementation(libs.kmpalette.androidx.palette)
}
}
}
}val palette = Palette.from(bitmap).generate()
val vibrant = palette.vibrantSwatch
val dominant = palette.dominantSwatchThe core module includes androidx-palette as a transitive dependency, so you don't need to add
both. Use androidx-palette on its own when you want palette generation without pulling in Compose.
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 dominant Color object.PaletteState - A state object that holds a generated Palette object.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-bytearray module has been removedextensions-file now uses FileKit instead of OkioIf 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.
For the remaining code see LICENSE for more information.
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.
This library is written for Compose Multiplatform and can be used on the following platforms:
| Artifact | Android | Desktop | iOS | macOS | JS | WASM |
|---|---|---|---|---|---|---|
androidx-palette |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
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.materialkolor.palette:core", version.ref = "kmpalette" }
# Optional - standalone palette generation without Compose
kmpalette-androidx-palette = { module = "com.materialkolor.palette:androidx-palette", version.ref = "kmpalette" }
# Optional source libraries
kmpalette-extensions-base64 = { module = "com.materialkolor.palette:extensions-base64", version.ref = "kmpalette" }
kmpalette-extensions-network = { module = "com.materialkolor.palette:extensions-network", version.ref = "kmpalette" }
kmpalette-extensions-file = { module = "com.materialkolor.palette: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 (includes Compose utilities)
implementation(libs.kmpalette.core)
// Or use just the palette generation without Compose
// implementation(libs.kmpalette.androidx.palette)
// Optional extensions based on your image source
implementation(libs.kmpalette.extensions.base64)
implementation(libs.kmpalette.extensions.network)
implementation(libs.kmpalette.extensions.file)
}
}
}
}The androidx-palette module is a full Kotlin Multiplatform port of
the androidx.palette library. It
has no dependency on Compose and can be used in any Kotlin Multiplatform project.
If you don't need the Compose utilities provided by core, you can depend on androidx-palette
directly to generate color palettes from raw pixel data:
kotlin {
sourceSets {
commonMain {
dependencies {
implementation(libs.kmpalette.androidx.palette)
}
}
}
}val palette = Palette.from(bitmap).generate()
val vibrant = palette.vibrantSwatch
val dominant = palette.dominantSwatchThe core module includes androidx-palette as a transitive dependency, so you don't need to add
both. Use androidx-palette on its own when you want palette generation without pulling in Compose.
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 dominant Color object.PaletteState - A state object that holds a generated Palette object.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-bytearray module has been removedextensions-file now uses FileKit instead of OkioIf 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.
For the remaining code see LICENSE for more information.