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.
You can checkout a web demo at demo.kmpalette.com.
- Platforms
- Inspiration
- Dynamic Material Themes
- Setup
- Usage
- Demo
- Feature Requests
- Contributing
- License
This library is written for Compose Multiplatform, and can be used on the following platforms:
Artifact | Android | Desktop | iOS | macOS | Browser |
---|---|---|---|---|---|
core |
β | β | β | β | β |
extensions-base64 |
β | β | β | β | β |
extensions-bytearray |
β | β | β | β | β |
extensions-libres |
β | β | β | β | β |
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 = "3.1.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-bytearray = { module = "com.kmpalette:extensions-bytearray", version.ref = "kmpalette" }
kmpalette-extensions-libres = { module = "com.kmpalette:extensions-libres", 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.bytearray)
implementation(libs.kmpalette.extensions.libres)
implementation(libs.kmpalette.extensions.network)
implementation(libs.kmpalette.extensions.file)
}
}
}
}
To see the generated KDocs, visit docs.kmpalette.com
In order 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 @Composeable
-ready State objects:
DominantColorState
- A state object that holds a generated dominantColor
object.PaletteState
- A state object that holds a generatedPalette
object.
They can be used like so:
You can generate a dominant color from an ImageBitmap
using the rememberDominantColorState
composeable. 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 by specifying the DominantColorState.loader
parameter:
@Composable
fun SomeComposable(painter: Painter) {
val loader = rememberPainterLoader()
val dominantColorState = rememberDominantColorState(loader = loader)
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)
}
For more examples of getting a dominant color see the demo app
If you want a whole color palette instead of just a dominate color, you can use
the rememberPaletteState
composeable. This will provide a Palette
object which contains a few
different color Swatch
s, each have their own color and onColor.
Using an ImageBitmap
:
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(
"Some Text",
color = paletteState.vibrantSwatch?.onColor ?: Color.Black,
)
}
}
Or using a Painter
:
fun SomeComposable(painter: Painter) {
val loader = rememberPainterLoader()
val paletteState = rememberPaletteState(loader = loader)
LaunchedEffect(painter) {
paletteState.generate(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.
For more examples of generating a Palette see the demo app
The kmpalette-core
library provides the core functionality for generating color palettes from
a ImageBitmap
or a Painter
object.
This library provides some extensions artifacts for some popular sources.
Artifact | Library | Loader | Input Class | Demo |
---|---|---|---|---|
extensions-base64 |
N/A | Base64Loader |
String |
Base64DemoScreen |
extensions-bytearray |
N/A | ByteArrayLoader |
ByteArray |
N/A |
extensions-libres |
libres | LibresLoader |
Image |
LibresPaletteScreen |
extensions-network |
ktor | NetworkLoader |
Url |
NetworkDemoScreen |
extensions-file |
okio | PathLoader , FilePathLoader |
Path , String |
N/A |
Each of these extensions provides a ImageBitmapLoader
object that can be used to generate
an ImageBitmap
from the input class. For example, the NetworkLoader
can be used to generate
an ImageBitmap
from a Url
:
@Composable
fun SomeComposable(url: Url) {
val networkLoader = rememberNetworkLoader()
val dominantColorState = rememberDominantColorState(loader = networkLoader)
LaunchedEffect(url) {
dominantColorState.updateFrom(url)
}
Box(
modifier = Modifier
.width(200.dp)
.height(100.dp)
.background(dominantColorState.color)
) {
Text("Some Text", color = dominantColorState.onColor)
}
}
As of Compose Multiplatform 1.6.0
the way Resources are handled has changed. Thus this library has
deleted it's extension as it is no longer needed.
To generate a palette from a DrawableResource
you can use the @Composable imageResource()
to get
an ImageBitmap
then pass that to the default loader.
@Composable
fun MyComposeable() {
val image = imageResource(R.drawable.my_image)
val dominantColorState = rememberDominantColorState()
LaunchedEffect(url) {
dominantColorState.updateFrom(image)
}
}
A demo app is available in the demo
directory. It is a Compose Multiplatform app that runs on
Android, iOS and Desktop.
See the README for more information.
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.