Kamel is an asynchronous media loading library for Compose. It provides a simple, customizable and efficient way to load, cache, decode and display images in your application. By default, it uses Ktor client for loading resources.
Kamel is published on Maven Central:
repositories {
mavenCentral()
// ...
}
Add the dependency to the common source-set:
kotlin {
sourceSets {
commonMain {
dependencies {
implementation("com.alialbaali.kamel:kamel-image:0.2.2")
// ...
}
}
}
}
Add the dependency to the dependencies block:
dependencies {
implementation("com.alialbaali.kamel:kamel-image:0.2.2")
// ...
}
To load an image, you can use lazyImageResource
composable, it can load images from different data sources.
// String
lazyImageResource(data = "https://www.example.com/image.jpg")
// Ktor Url
lazyImageResource(data = Url("https://www.example.com/image.jpg"))
// URI
lazyImageResource(data = URI("https://www.example.com/image.png"))
// File
lazyImageResource(data = File("/path/to/image.png"))
// URL
lazyImageResource(data = URL("https://www.example.com/image.jpg"))
// and more...
Since there isn't any shared resource system between Android and Desktop, some implementations (e.g. fetchers and mappers) are only available for a specific platform:
To load an image file from desktop application resources, you have to add resourcesFetcher
to the KamelConfig
:
val desktopConfig = KamelConfig {
takeFrom(KamelConfig.Default)
resourcesFetcher() // Available only on Desktop.
}
Assuming there's an image.png
file in the /resources
directory in the project:
CompositionLocalProvider(LocalKamelConfig provides desktopConfig) {
lazyImageResource("image.png")
}
To load an image file from android application resources, you have to add resourcesFetcher
and resourcesIdMapper
to
the KamelConfig
:
val context : Context = LocalContext.current
val androidConfig = KamelConfig {
takeFrom(KamelConfig.Default)
resourcesFetcher(context) // Available only on Android.
resourcesIdMapper(context) // Available only on Android.
}
Assuming there's an image.png
file in the /res/raw
directory in the project:
CompositionLocalProvider(LocalKamelConfig provides androidConfig) {
lazyImageResource(R.raw.image)
}
lazyImageResource
supports configuration using trailing lambda:
val imageResource: Resource<ImageBitmap> = lazyImageResource("https://www.example.com/image.jpg") {
dispatcher = Dispatchers.IO // Coroutine Dispatcher to be used while loading.
requestBuilder { // this: HttpRequestBuilder
header("Key", "Value")
parameter("Key", "Value")
cacheControl(CacheControl.MAX_AGE)
}
}
KamelImage
is a composable function that takes an ImageBitmap
resource, display it and provide extra
functionality:
KamelImage(
resource = imageResource,
contentDescription = "Profile"
)
KamelImage
can display custom content in failure or loading states through onFailure
and onLoading
parameters:
KamelImage(
resource = imageResource,
contentDescription = "Profile",
onLoading = {
Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator()
}
},
onFailure = { exception ->
Snackbar {
Text(exception.message)
}
}
)
You can also provide your own custom implementation using a simple when expression:
when (val resource = lazyImageResource("https://www.example.com/image.jpg")) {
is Resource.Loading -> {
Text("Loading...")
}
is Resource.Success -> {
val bitmap: ImageBitmap = resource.value
Image(bitmap, null, modifier = Modifier.clip(CircleShape))
}
is Resource.Failure -> {
log(resource.exception)
val fallbackImage = imageResource("/path/to/fallbackImage.jpg")
Image(fallbackImage, null)
}
}
You can enable, disable or customize crossfade (fade-in) animation through the crossfade
and animationSpec
parameters:
KamelImage(
resource = imageResource,
contentDescription = "Profile",
crossfade = true, // false by default
animationSpec = tween(),
)
The default implementation is KamelConfig.Default
. If you wish to configure it, you can do it like so:
val myKamelConfig = KamelConfig {
imageBitmapDecoder() // adds an ImageBitmapDecoder
fileFetcher() // adds a FileFetcher
httpFetcher { // Configuring Ktor HttpClient
defaultRequest {
url("https://www.example.com/")
cacheControl(CacheControl.MaxAge(maxAgeSeconds = 10000))
}
Logging {
level = LogLevel.INFO
logger = Logger.SIMPLE
}
}
// more functionality available.
}
To Configure memory cache size, you can change the imageBitmapCacheSize
property:
KamelConfig {
imageBitmapCacheSize = 1000
}
You can use LocalKamelConfig
to apply your custom configuration:
CompositionLocalProvider(LocalKamelConfig provides myKamelConfig) {
lazyImageResource("image.jpg")
}
Contributions are always welcome!. If you'd like to contribute, please feel free to create a PR.
Copyright 2021 Ali Albaali
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.