https://github.com/skydoves/landscapist
🌻 A pluggable, highly optimized Jetpack Compose and Kotlin Multiplatform image loading library that fetches and displays network images, compatible with Glide, Coil, and Fresco.
https://github.com/skydoves/landscapist
android compose compose-coil compose-fresco compose-glide image image-loading jetpack-compose kotlin landscapist library network skydoves
Last synced: 3 months ago
JSON representation
🌻 A pluggable, highly optimized Jetpack Compose and Kotlin Multiplatform image loading library that fetches and displays network images, compatible with Glide, Coil, and Fresco.
- Host: GitHub
- URL: https://github.com/skydoves/landscapist
- Owner: skydoves
- License: apache-2.0
- Created: 2020-09-24T16:18:03.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2026-01-28T11:05:40.000Z (3 months ago)
- Last Synced: 2026-01-28T21:32:10.651Z (3 months ago)
- Topics: android, compose, compose-coil, compose-fresco, compose-glide, image, image-loading, jetpack-compose, kotlin, landscapist, library, network, skydoves
- Language: Kotlin
- Homepage: https://skydoves.github.io/landscapist/
- Size: 31.8 MB
- Stars: 2,490
- Watchers: 12
- Forks: 135
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
- kmp-awesome - Landscapist
- fucking-awesome-android-ui - Landscapist - 2.0) | <img src="https://user-images.githubusercontent.com/24237865/95661452-6abad480-0b6a-11eb-91c4-7cbe40b77927.gif" width="46%"> <img src="https://user-images.githubusercontent.com/24237865/95812167-be3a4780-0d4f-11eb-9360-2a4a66a3fb46.gif" width="46%"> (Index)
- awesome-android-ui - Landscapist - 2.0) | <img src="https://user-images.githubusercontent.com/24237865/95661452-6abad480-0b6a-11eb-91c4-7cbe40b77927.gif" width="46%"> <img src="https://user-images.githubusercontent.com/24237865/95812167-be3a4780-0d4f-11eb-9360-2a4a66a3fb46.gif" width="46%"> (Index)
- awesome-list - skydoves/landscapist - 🌻 A pluggable, highly optimized Jetpack Compose and Kotlin Multiplatform image loading library that fetches and displays network images, compatible with Glide, Coil, and Fresco. (Kotlin)
README

🌻 Landscapist is a highly optimized, pluggable Jetpack Compose and Kotlin Multiplatform image loading solution that fetches and displays network images, and compatibles with Glide, Coil, and Fresco. This library supports tracing image loading states, composing custom implementations, and some valuable animations, such as crossfades, blur transformation, and circular reveals. You can also configure and attach image-loading behaviors easily and fast with image plugins.
See official documentation for Landscapist
## Who's using Landscapist?
👉 [Check out who's using Landscapist](https://skydoves.github.io/landscapist/#whos-using-landscapist).
Landscapist hits **+1,100,000 downloads every month** around the globe!

## Why Landscapist?
Landscapist is built with a lot of consideration to improve the performance of image loadings in Jetpack Compose. Most composable functions of Landscapist are Restartable and Skippable, which indicates fairly improved recomposition performance according to the Compose compiler metrics. Also, the library performance was improved with [Baseline Profiles](https://android-developers.googleblog.com/2022/01/improving-app-performance-with-baseline.html) and it supports many pluggable features, such as [ImageOptions](https://github.com/skydoves/landscapist#imageoptions), [listening image state changes](https://github.com/skydoves/landscapist#listening-image-state-changes), [custom composables](https://github.com/skydoves/landscapist#custom-composables), [preview on Android Studio](https://github.com/skydoves/landscapist#preview-on-android-studio), [ImageComponent and ImagePlugin](https://github.com/skydoves/landscapist#imagecomponent-and-imageplugin), [placeholder](https://github.com/skydoves/landscapist#placeholder), [animations (circular reveal, crossfade)](https://github.com/skydoves/landscapist#animation), [transformation (blur)](https://github.com/skydoves/landscapist#transformation), [palette](https://github.com/skydoves/landscapist#palette), and [zoomable](https://github.com/skydoves/landscapist#zoomable).
See the Compose compiler metrics for Landscapist

## Documentation
For comprehensive information about Landscapist, please refer to the **[official documentation](https://skydoves.github.io/landscapist)**.
## Technical Content
For more details of the history, performance, customizability, and how to load network images and implement placeholders, animations, and transformations with Landscapist, check out [Optimized Image Loading for Compose and Kotlin Multiplatform](https://medium.com/proandroiddev/optimized-image-loading-for-compose-and-kotlin-multiplatform-a45eb2e710c0).
## 💝 Sponsors
## Demo projects
You can see the use cases of this library in the repositories below:
- [google/modernstorage](https://github.com/google/modernstorage/tree/e62cda539ca75884dd49df3bcf8629751f0a91e6/sample): ModernStorage is a group of libraries that provide an abstraction layer over storage on Android to simplify its interactions.
- [GetStream/WhatsApp-Clone-Compose](https://github.com/getStream/whatsApp-clone-compose): 📱 WhatsApp clone project demonstrates modern Android development built with Jetpack Compose and Stream Chat SDK for Compose.
- [android/storage-samples](https://github.com/android/storage-samples/tree/main/ScopedStorage): Multiple samples showing the best practices in storage APIs on Android.
- [skydoves/DisneyCompose](https://github.com/skydoves/disneycompose): 🧸 A demo Disney app using Jetpack Compose and Hilt based on modern Android tech-stacks and MVVM architecture.
- [skydoves/MovieCompose](https://github.com/skydoves/MovieCompose): 🎞 A demo movie app using Jetpack Compose and Hilt based on modern Android tech stacks.
## Landscapist Core & Image
Landscapist now provides two foundational modules designed for Kotlin Multiplatform and Compose Multiplatform from the scratch, giving you full control over image loading across all platforms:
- **`landscapist-core`**: A standalone, Kotlin Multiplatform image loading engine with built-in memory/disk caching, progressive loading, and network fetching via Ktor. Works on Android, iOS, Desktop, and Web without any UI dependencies.
- **`landscapist-image`**: A Compose Multiplatform image component built on top of `landscapist-core` with full plugin support. Seamlessly works across all Compose Multiplatform targets.
These modules are perfect if you want a lightweight, customizable image loader without depending on Glide, Coil, or Fresco, with first-class support for all Kotlin Multiplatform and Compose Multiplatform targets.
Landscapist Core
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
The `landscapist-core` module is a complete, Kotlin Multiplatform image loading solution that works standalone without any UI dependencies. It provides:
- **Network image loading** via Ktor HTTP client.
- **Memory caching** with LRU eviction and weak references.
- **Disk caching** with size limits and automatic cleanup.
- **Progressive loading** for better perceived performance.
- **Priority-based scheduling** for optimized resource usage.
- **Kotlin Multiplatform** support (Android, iOS, Desktop, Web).
- **Image transformations** and custom decoders.
- **Event listeners** for monitoring load states.
### Why Choose Landscapist Core?
Landscapist Core is **exceptionally lightweight** compared to other image loading libraries, making it the ideal choice for SDK and library developers who need to minimize their dependency footprint.
**AAR Size Comparison (Android Release Build):**
| Library | AAR Size | vs Landscapist Core | Impact on APK |
|---------|----------|---------------------|---------------|
| **landscapist-core** | **~312 KB** | **Baseline (Smallest)** | Minimal |
| Coil3 | ~460 KB | **+47% larger** | Moderate |
| Glide | ~689 KB | **+121% larger** | Significant |
| Fresco | ~1 MB | **+228% larger** | High |
**Performance (Android Release Build):**
| Library | Avg Load Time | Memory Usage | Supports KMP |
|---------|---------------|--------------|--------------|
| **LandscapistImage** | **1,245ms** | **4,520KB** | **✓** |
| **GlideImage** | 1,312ms (+5%) | 5,124KB (+13%) | ✗ |
| **CoilImage** | 1,389ms (+12%) | 4,876KB (+8%) | **✓** |
| **FrescoImage** | 1,467ms (+18%) | 5,342KB (+18%) | ✗ |
> For detailed performance analysis and test methodology, see the [Landscapist Core documentation](https://skydoves.github.io/landscapist/landscapist-core/#performance-comparison).
### Setup
Add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
implementation("com.github.skydoves:landscapist-core:$version")
}
```
For Kotlin Multiplatform, add to your **module**'s `build.gradle.kts`:
```kotlin
sourceSets {
commonMain.dependencies {
implementation("com.github.skydoves:landscapist-core:$version")
}
}
```
All platform-specific Ktor engines are included automatically based on your target platforms.
### Using Landscapist Core for Network Loading
You can use `landscapist-core` as a standalone image loader for fetching and caching network images without any UI dependencies. This is useful for pre-loading images, implementing custom image components, or using images in non-Compose contexts.
#### Android Example
This example demonstrates creating a Landscapist instance with custom cache sizes and loading an image from a URL. The result is delivered as a Flow, allowing you to handle loading, success, and failure states reactively.
```kotlin
import com.skydoves.landscapist.core.Landscapist
import com.skydoves.landscapist.core.LandscapistConfig
import com.skydoves.landscapist.core.ImageRequest
import kotlinx.coroutines.flow.collect
// Create a Landscapist instance (typically once in your app)
val landscapist = Landscapist.builder(context)
.config(
LandscapistConfig(
memoryCacheSize = 64 * 1024 * 1024L, // 64MB
diskCacheSize = 100 * 1024 * 1024L, // 100MB
)
)
.build()
// Load an image
lifecycleScope.launch {
val request = ImageRequest.builder()
.model("https://example.com/image.jpg")
.size(width = 800, height = 600)
.build()
landscapist.load(request).collect { result ->
when (result) {
is ImageResult.Loading -> {
// Show loading state
}
is ImageResult.Success -> {
val imageBitmap = result.data
// Use the loaded ImageBitmap
}
is ImageResult.Failure -> {
// Handle error
}
}
}
}
```
#### Kotlin Multiplatform Example
For non-Android platforms (iOS, Desktop, Web), use the singleton instance which comes pre-configured with sensible defaults. This example shows a simple suspend function that loads an image and returns the ImageBitmap, suitable for use in shared Kotlin Multiplatform code.
```kotlin
import com.skydoves.landscapist.core.Landscapist
import com.skydoves.landscapist.core.ImageRequest
// Get the default instance (works on all platforms)
val landscapist = Landscapist.getInstance()
suspend fun loadImage(url: String): ImageBitmap? {
val request = ImageRequest.builder()
.model(url)
.build()
var bitmap: ImageBitmap? = null
landscapist.load(request).collect { result ->
if (result is ImageResult.Success) {
bitmap = result.data
}
}
return bitmap
}
```
### Advanced Configuration
Customize the Landscapist instance with advanced options including network timeouts, memory optimizations, and event listeners. This example shows how to configure various aspects of the image loader to match your app's specific requirements.
```kotlin
val landscapist = Landscapist.builder(context)
.config(
LandscapistConfig(
// Memory cache
memoryCacheSize = 64 * 1024 * 1024L,
// Disk cache
diskCacheSize = 100 * 1024 * 1024L,
// Network settings
networkConfig = NetworkConfig(
connectTimeout = 10.seconds,
readTimeout = 30.seconds,
userAgent = "MyApp/1.0"
),
// Performance optimizations
allowRgb565 = true, // Use less memory for images without transparency
weakReferencesEnabled = true,
// Event listener
eventListenerFactory = EventListener.Factory { request ->
object : EventListener {
override fun onStart(request: ImageRequest) {
println("Started loading: ${request.model}")
}
override fun onSuccess(request: ImageRequest, result: ImageResult.Success) {
println("Loaded from: ${result.dataSource}")
}
}
}
)
)
.build()
```
Landscapist Image
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
The `landscapist-image` module provides a Compose Multiplatform UI component built on top of `landscapist-core`. It integrates seamlessly with the Landscapist plugin ecosystem and works across all Compose Multiplatform targets (Android, iOS, Desktop, Web).
### Setup
Add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
implementation("com.github.skydoves:landscapist-image:$version")
}
```
> **Note**: This module depends on `landscapist-core`, which includes Ktor client automatically. No need to add Ktor dependencies separately.
For Kotlin Multiplatform:
```kotlin
sourceSets {
commonMain.dependencies {
implementation("com.github.skydoves:landscapist-image:$version")
}
}
```
### LandscapistImage
Load images in Compose using `LandscapistImage`:
```kotlin
import com.skydoves.landscapist.image.LandscapistImage
import com.skydoves.landscapist.ImageOptions
LandscapistImage(
imageModel = { "https://example.com/image.jpg" },
modifier = Modifier.size(300.dp),
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
)
)
```
### With Plugins
`LandscapistImage` supports all Landscapist plugins:
```kotlin
import com.skydoves.landscapist.components.rememberImageComponent
import com.skydoves.landscapist.placeholder.shimmer.ShimmerPlugin
import com.skydoves.landscapist.animation.crossfade.CrossfadePlugin
import com.skydoves.landscapist.transformation.blur.BlurTransformationPlugin
LandscapistImage(
imageModel = { imageUrl },
modifier = Modifier.fillMaxWidth(),
component = rememberImageComponent {
+ShimmerPlugin(
baseColor = Color.Gray,
highlightColor = Color.LightGray
)
+CrossfadePlugin(duration = 550)
+BlurTransformationPlugin(radius = 10)
}
)
```
### Custom Landscapist Instance
Provide a custom `Landscapist` instance to your composition tree:
```kotlin
import com.skydoves.landscapist.core.Landscapist
import com.skydoves.landscapist.image.LocalLandscapist
import androidx.compose.runtime.CompositionLocalProvider
// Create custom instance
val customLandscapist = Landscapist.builder(context)
.config(
LandscapistConfig(
memoryCacheSize = 128 * 1024 * 1024L // 128MB
)
)
.build()
// Provide to composition
CompositionLocalProvider(LocalLandscapist provides customLandscapist) {
LandscapistImage(
imageModel = { imageUrl },
// Will use the custom instance
)
}
```
### Loading States
Handle loading, success, and failure states:
```kotlin
LandscapistImage(
imageModel = { imageUrl },
loading = {
Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
},
success = { state, painter ->
Image(
painter = painter,
contentDescription = "Loaded image"
)
},
failure = {
Text("Failed to load image")
}
)
```
### Image State Changes
Monitor state changes with a callback:
```kotlin
var currentState by remember { mutableStateOf(LandscapistImageState.None) }
LandscapistImage(
imageModel = { imageUrl },
onImageStateChanged = { state ->
currentState = state
}
)
when (currentState) {
is LandscapistImageState.Loading -> { /* loading */ }
is LandscapistImageState.Success -> { /* success */ }
is LandscapistImageState.Failure -> { /* failure */ }
else -> { /* none */ }
}
```
### Supported Image Sources
`LandscapistImage` supports various image sources including network URLs, local files, drawable resources, and more. See the [Landscapist Image documentation](https://skydoves.github.io/landscapist/landscapist-image/#supported-image-sources) for a complete list of supported image sources per platform.
---
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
Add the codes below to your **root** `build.gradle` file (not your module-level build.gradle file):
```gradle
allprojects {
repositories {
mavenCentral()
}
}
```
Next, add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
implementation("com.github.skydoves:landscapist-glide:2.9.5")
}
```
> **Note**: `Landscapist-Glide` includes version `4.16.0` of [Glide](https://github.com/bumptech/glide) internally. So please make sure your project is using the same Glide version or exclude the Glide dependency to adapt yours. Also, please make sure the Jetpack Compose version on the [release page](https://github.com/skydoves/Landscapist/releases).
### GlideImage
You can load images simply by using `GlideImage` composable function as the following example below:
```kotlin
GlideImage(
imageModel = { imageUrl }, // loading a network image using an URL.
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
)
)
```
### More Details for GlideImage
👉 Read further for more details
### Custom RequestOptions and TransitionOptions
You can customize your request-options with your own [RequestOptions](https://bumptech.github.io/glide/doc/options.html#requestoptions) and [TransitionOptions](https://bumptech.github.io/glide/doc/options.html#transitionoptions) for applying caching strategies, loading transformations like below:
```kotlin
GlideImage(
imageModel = { imageUrl },
requestOptions = {
RequestOptions()
.override(256, 256)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.centerCrop()
}
)
```
### Custom RequestBuilder
You can request image with your own [RequestBuilder](https://bumptech.github.io/glide/doc/options.html#requestbuilder), which is the backbone of the request in Glide and is responsible for bringing your options together with your requested url or model to start a new load.
```kotlin
GlideImage(
imageModel = { imageUrl },
requestBuilder = { Glide.with(LocalContext.current.applicationContext).asDrawable() },
modifier = Modifier.constrainAs(image) {
centerHorizontallyTo(parent)
top.linkTo(parent.top)
}.aspectRatio(0.8f)
)
```
### Custom RequestListener
You can register your own [RequestListener](https://bumptech.github.io/glide/javadocs/440/com/bumptech/glide/request/RequestListener.html), which allows you to trace the status of a request while images load.
```kotlin
GlideImage(
imageModel = { imageUrl },
requestListener = object: RequestListener {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target?,
isFirstResource: Boolean
): Boolean {
// do something
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
// do something
return true
}
}
)
```
### LocalGlideRequestOptions
You can pass the same instance of your `RequestOptions` down through the Composition in your composable hierarchy as following the example below:
```kotlin
val requestOptions = RequestOptions()
.override(300, 300)
.circleCrop()
CompositionLocalProvider(LocalGlideRequestOptions provides requestOptions) {
// Loads images with the custom `requestOptions` without explicit defines.
GlideImage(
imageModel = ...
)
}
```
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
Add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
implementation("com.github.skydoves:landscapist-coil:$version")
}
```
If you're targeting on Kotlin Multiplatform, add the dependency below to your module's `build.gradle.kts` file:
```kotlin
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.github.skydoves:landscapist-coil3:$version")
}
}
}
```
The `coil3-landscapist` package functions identically to the `coil-landscapist` package, with the key distinction being its focus on Kotlin Multiplatform. This enables the use of Coil3 across various platforms, including Android, iOS, and Desktop (JVM), facilitating a unified image loading experience across different environments.
> **Note**: Please make sure your project uses the same Jetpack Compose version on the [release page](https://github.com/skydoves/Landscapist/releases).
### CoilImage
You can load images by using the `CoilImage` composable function as the following example below:
```kotlin
CoilImage(
imageModel = { imageUrl }, // loading a network image or local resource using an URL.
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
)
)
```
### More Details for CoilImage
👉 Read further for more details
### Custom ImageRequest and ImageLoader
You can load images with your own [ImageRequest](https://coil-kt.github.io/coil/image_requests/) and [ImageLoader](https://coil-kt.github.io/coil/image_loaders/), which provides all the necessary information for loading images like caching strategies and transformations.
```kotlin
CoilImage(
imageRequest = {
ImageRequest.Builder(LocalContext.current)
.data(poster.poster)
.crossfade(true)
.build() },
imageLoader = {
ImageLoader.Builder(LocalContext.current)
.availableMemoryPercentage(0.25)
.crossfade(true)
.build() },
modifier = modifier,
)
```
### LocalCoilImageLoader
You can pass the same instance of your `ImageLoader` down through the Composition in your composable hierarchy as following the example below:
```kotlin
val imageLoader = ImageLoader.Builder(context).build()
CompositionLocalProvider(LocalCoilImageLoader provides imageLoader) {
// This will automatically use the value of current imageLoader in the hierarchy.
CoilImage(
imageModel = ...
)
}
```

## Animated Image Supports (GIF, Webp)
You can load animated GIFs and WebP Images with your `ImageLoader`.
```kotlin
val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
.components {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.build()
CoilImage(
imageModel = { poster.gif }, // URL of an animated image.
imageLoader = { imageLoader },
shimmerParams = ShimmerParams(
baseColor = background800,
highlightColor = shimmerHighLight
),
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(500.dp)
.clip(RoundedCornerShape(8.dp))
)
```
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
Add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
implementation("com.github.skydoves:landscapist-fresco:$version")
}
```
> **Note**: `Landscapist-Fresco` includes version `3.1.0` of Fresco. So please make sure your project is using the same Fresco version or exclude the Fresco dependency to adapt yours. Also, please make sure the Jetpack Compose version on the [release page](https://github.com/skydoves/Landscapist/releases).
### Setup
To get started, you should set up `Fresco` with [ImagePipelineConfig](https://frescolib.org/docs/configure-image-pipeline.html) in your `Application` class. Generally, it's recommended initializing with `OkHttpImagePipelineConfigFactory`. Also, you can customize caching, networking, and thread pool strategies with your own `ImagePipelineConfig`. For more details, you can check out [Using Other Network Layers](https://frescolib.org/docs/using-other-network-layers.html#using-okhttp).
```kotlin
class App : Application() {
override fun onCreate() {
super.onCreate()
val pipelineConfig =
OkHttpImagePipelineConfigFactory
.newBuilder(this, OkHttpClient.Builder().build())
.setDiskCacheEnabled(true)
.setDownsampleEnabled(true)
.setResizeAndRotateEnabledForNetwork(true)
.build()
Fresco.initialize(this, pipelineConfig)
}
}
```
### FrescoImage
You can load images by using the `FrescoImage` composable function as the following example below:
```kotlin
FrescoImage(
imageUrl = stringImageUrl, // loading a network image using an URL.
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
)
)
```
### More Details for FrescoImage
👉 Read further for more details
### Custom ImageRequest
You can load images with your own [ImageRequest](https://frescolib.org/docs/image-requests.html), which provides some necessary information for loading images like decoding strategies and resizing.
```kotlin
val imageRequest = ImageRequestBuilder
.newBuilderWithSource(uri)
.setImageDecodeOptions(decodeOptions)
.setLocalThumbnailPreviewsEnabled(true)
.setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
.setProgressiveRenderingEnabled(false)
.setResizeOptions(ResizeOptions(width, height))
.build()
FrescoImage(
imageUrl = stringImageUrl,
imageRequest = { imageRequest }
)
```
### LocalFrescoImageRequest
You can pass the same instance of your `imageRequestBuilder` down through the Composition in your composable hierarchy as following the example below:
```kotlin
// customize the ImageRequest as needed
val imageRequestBuilder = ImageRequestBuilder
.newBuilderWithSource(uri)
.setImageDecodeOptions(decodeOptions)
.setLocalThumbnailPreviewsEnabled(true)
.setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
.setProgressiveRenderingEnabled(false)
.setResizeOptions(ResizeOptions(width, height))
CompositionLocalProvider(LocalFrescoImageRequest provides imageRequestBuilder) {
// This will automatically use the value of current ImageRequest in the hierarchy.
FrescoImage(
imageUrl = ...
)
}
```

## Fresco Animated Image Support (GIF, Webp)
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
Add the below dependency to your **module**'s `build.gradle` file.
```gradle
dependencies {
implementation("com.github.skydoves:landscapist-fresco-websupport:$version")
}
```
You can load animated GIFs and WebP Images with `FrescoWebImage` composable function. You should pass the `AbstractDraweeController` like the following example below:
```kotlin
FrescoWebImage(
controllerBuilder = {
Fresco.newDraweeControllerBuilder()
.setUri(poster.gif) // GIF or Webp image url.
.setAutoPlayAnimations(true)
},
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
)
```
For more details, check out [DraweeController](https://frescolib.org/docs/animations.html), and [Supported URIs](https://frescolib.org/docs/supported-uris.html) for setting URI addresses. Also, you can load general images (jpeg, png, etc) which can be loaded with `FrescoImage` by using `FrescoWebImage` and your custom controller.
## ImageOptions
You can give image options to your image composable functions by passing `ImageOptions` instance like the below:
```kotlin
GlideImage(
..
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center,
contentDescription = "main image",
colorFilter = null,
alpha = 1f
)
)
```
### RequestSize
You can set the request size of your image by giving `requestSize` property as seen in the below:
```kotlin
GlideImage(
..
imageOptions = ImageOptions(requestSize = IntSize(800, 600)),
)
```
## Listening image state changes
You can listen the image state changes by giving `onImageStateChanged` parameter to your image composable functions like the below:
```kotlin
GlideImage(
..
onImageStateChanged = {
when (it) {
GlideImageState.None -> ..
GlideImageState.Loading -> ..
is GlideImageState.Success -> ..
is GlideImageState.Failure -> ..
}
}
)
```
> **Note**: You can use `CoilImageState` for `CoilImage` and `FrescoImageState` for `FrescoImage`.
### DataSource
For the success state, you can trace the origin of the image with the `DataSource` parameter. `DataSource` represents the following source origins below:
- **Memory**: Represents an in-memory data source or cache (e.g. bitmap, ByteBuffer).
- **Disk**: Represents a disk-based data source (e.g. drawable resource, or File).
- **Network**: Represents a network-based data source.
- **Unknown**: Represents an unknown data source.

## Custom Composables
You can execute your own composable functions depending on the three request states below:
- **loading**: Executed while loading an image.
- **success**: Executed if loading an image successfully.
- **failure**: Executed if fails to load an image (e.g. network error, wrong destination).
```kotlin
GlideImage( // CoilImage, FrescoImage
imageModel = { imageUrl },
modifier = modifier,
// shows an indicator while loading an image.
loading = {
Box(modifier = Modifier.matchParentSize()) {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.Center)
)
}
},
// shows an error text if fail to load an image.
failure = {
Text(text = "image request failed.")
}
)
```
Also, you can customize the image content with our own composable function like the example below:
```kotlin
GlideImage( // CoilImage, FrescoImage
imageModel = { imageUrl },
success = { state, painter ->
Image(
painter = painter,
modifier = Modifier.size(128.dp), // draw a resized image.
contentDescription = "Image"
)
},
..
)
```
> **Note**: You can also use the custom Composables for **`CoilImage`** and **`FrescoImage`**.

## Preview on Android Studio
Landscapist supports preview mode for each image library; **Glide**, **Coil**, and **Fresco**. You can show the preview image on your editor with a `previewPlaceholder` parameter as following:
```kotlin
GlideImage(
imageModel = { imageUrl },
modifier = Modifier.aspectRatio(0.8f),
previewPlaceholder = painterResource(id = R.drawable.poster)
)
```
> **Note**: You can also use the the `previewPlaceholder` parameter for **`CoilImage`** and **`FrescoImage`**.
## ImageComponent and ImagePlugin
You can compose supported image plugins by Landscapist or you can create your own image plugin that will be composed following the image loading state.
`ImagePlugin` is a pluggable compose interface that will be executed for loading images. `ImagePlugin` provides following types below:
- **PainterPlugin**: A pinter plugin interface to be composed with the given `Painter`.
- **LoadingStatePlugin**: A pluggable state plugin that will be composed while the state is `ImageLoadState.Loading`.
- **SuccessStatePlugin**: A pluggable state plugin that will be composed when the state is `ImageLoadState.Success`.
- **FailureStatePlugin**: A pluggable state plugin that will be composed when the state is `ImageLoadState.Failure`.
For example, you can implement your own `LoadingStatePlugin` that will be composed while loading an image like the below:
```kotlin
data class LoadingPlugin(val source: Any?) : ImagePlugin.LoadingStatePlugin {
@Composable
override fun compose(
modifier: Modifier,
imageOptions: ImageOptions?
): ImagePlugin = apply {
if (source != null && imageOptions != null) {
ImageBySource(
source = source,
modifier = modifier,
alignment = imageOptions.alignment,
contentDescription = imageOptions.contentDescription,
contentScale = imageOptions.contentScale,
colorFilter = imageOptions.colorFilter,
alpha = imageOptions.alpha
)
}
}
}
```
Next, you can compose plugins by adding them in the `rememberImageComponent` like the below:
```kotlin
GlideImage(
imageModel = { poster.image },
component = rememberImageComponent {
add(CircularRevealPlugin())
add(LoadingPlugin(source))
},
)
```
or you can just add plugins by using the **+** expression like the below:
```kotlin
GlideImage(
imageModel = { poster.image },
component = rememberImageComponent {
+CircularRevealPlugin()
+LoadingPlugin(source)
},
)
```
### LocalImageComponent
You can provide the same `ImageComponent` instance in the composable hierarchy by using `imageComponent` extension and `LocalImageComponent` like the below:
```kotlin
val component = imageComponent {
+CrossfadePlugin()
+PalettePlugin()
}
CompositionLocalProvider(LocalImageComponent provides component) {
..
}
```
## Placeholder
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
The `landscapist-placeholder` package provides useful image plugins, such as loading & failure placeholder supports and shimmering animation.
To use placeholder supports, add the dependency below:
```kotlin
dependencies {
implementation("com.github.skydoves:landscapist-placeholder:$version")
}
```

### ShimmerPlugin
You can implement a shimmering effect while loading an image by using the `ShimmerPlugin` as following the example below:
```kotlin
GlideImage( // CoilImage, FrescoImage
imageModel = { imageUrl },
modifier = modifier,
component = rememberImageComponent {
// shows a shimmering effect when loading an image.
+ShimmerPlugin(
Shimmer.Flash(
baseColor = Color.White,
highlightColor = Color.LightGray,
),
)
},
// shows an error text message when request failed.
failure = {
Text(text = "image request failed.")
}
)
```
> **Note**: You can also use the Shimmer effect for **`CoilImage`** and **`FrescoImage`**.
`Shimmer` sealed class provides following the three different types: `Resonate`, `Fade`, and `Flash`.
| Resonate | Fade | Flash |
|:-------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------:|
|
|
|
|
### PlaceholderPlugin
You can show your own placeholder while loading an image or when fails to load an image with `PlaceholderPlugin.Loading` and `PlaceholderPlugin.Failure`.
```kotlin
GlideImage(
..
component = rememberImageComponent {
+PlaceholderPlugin.Loading(painterResource(id = R.drawable.placeholder_loading))
+PlaceholderPlugin.Failure(painterResource(id = R.drawable.placeholder_failure))
},
)
```
> **Note**: The source should be one of `ImageBitmap`, `ImageVector`, or `Painter`.
### ThumbnailPlugin

Landscapist supports the thumbnail feature, which pre-loads and displays small sizes of images while loading the original image. So you can make users feel images loading faster and give images a nature loading effect while displaying an original image.
To show thumbnail, add the image plugin into your image component like the example below:
```kotlin
GlideImage(
..,
component = rememberImageComponent {
+ThumbnailPlugin()
},
)
```
You can also adjust the request sizes by giving the `requestSize` parameter:
```kotlin
component = rememberImageComponent {
+ThumbnailPlugin(IntSize(30 ,30))
},
```
> **Note**: It's highly recommended to use a small size of the request size on the thumbnail plugin to load the pre-load images process faster.
## Animation
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
The `landscapist-animation` package provides useful image plugins related to animations, such as crossfade and circular reveal animation.
To use animation supports, add the dependency below:
```kotlin
dependencies {
implementation("com.github.skydoves:landscapist-animation:$version")
}
```
### Preview
| Circular Reveal | Crossfade |
|:----------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------:|
|
|
|
### Crossfade Animation
You can implement the crossfade animation while drawing images with `CrossfadePlugin` as the following:
```kotlin
GlideImage(
imageModel = { poster.image },
component = rememberImageComponent {
+CrossfadePlugin(
duration = 550
)
}
)
```
> **Note**: You can also use the crossfade animation for **`CoilImage`** and **`FrescoImage`**.
### Circular Reveal Animation
You can implement the circular reveal animation while drawing images with `CircularRevealplugin` as the following:
```kotlin
GlideImage(
imageModel = { poster.image },
component = rememberImageComponent {
+CircularRevealPlugin(
duration = 350
)
}
)
```
> **Note**: You can also use the Circular Reveal animation for **`CoilImage`** and **`FrescoImage`**.
## Transformation
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)

The `landscapist-transformation` package provides useful image transformation plugins, such as the blur effect.
To use transformation supports, add the dependency below:
```kotlin
dependencies {
implementation("com.github.skydoves:landscapist-transformation:$version")
}
```
### BlurTransformationPlugin
You can implement the blur effect with `BlurTransformationPlugin` as the following:
```kotlin
GlideImage( // CoilImage, FrescoImage also can be used.
imageModel = { poster.image },
component = rememberImageComponent {
+BlurTransformationPlugin(radius = 10) // between 0 to Int.MAX_VALUE.
}
)
```
>**Note**: Landscapist's blur transformation falls back onto a CPU-based implementation to support older API levels. So you don't need to worry about API compatibilities and performance issues.
## Palette
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
The `landscapist-palette` package provides useful image plugins related to palette, such as extracting primary color sets.
To use palette supports, add the dependency below:
```kotlin
dependencies {
implementation("com.github.skydoves:landscapist-palette:$version")
}
```
You can extract primary (theme) color profiles with `PalettePlugin`. You can check out [Extract color profiles](https://developer.android.com/training/material/palette-colors#extract-color-profiles) to see what kinds of colors can be extracted.

```kotlin
var palette by rememberPaletteState(null)
GlideImage( // CoilImage, FrescoImage also can be used.
imageModel = { poster.image },
component = rememberImageComponent {
+PalettePlugin { palette = it }
}
)
Crossfade(
targetState = palette,
modifier = Modifier
.padding(horizontal = 8.dp)
.size(45.dp)
) {
Box(
modifier = Modifier
.background(color = Color(it?.lightVibrantSwatch?.rgb ?: 0))
.fillMaxSize()
)
}
```
Also, you can customize attributes of `PalettePlugin` like the example below:
```kotlin
var palette by remember { mutableStateOf(null) }
GlideImage( // CoilImage, FrescoImage also can be used.
imageModel = { poster.image },
component = rememberImageComponent {
+PalettePlugin(
imageModel = poster.image,
useCache = true, // use cache strategies for the same image model.
interceptor = {
it.addFilter { rgb, hsl ->
// here edit to add the filter colors.
false
}
},
paletteLoadedListener = {
palette = it
}
)
}
)
```
> **Note**: You can also use the Palette for **`CoilImage`** and **`FrescoImage`**.
## Zoomable
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)

The `landscapist-zoomable` package provides a `ZoomablePlugin` that enables zoom and pan gestures for images. This plugin supports both Android and Kotlin Multiplatform (iOS, Desktop).
To use zoomable supports, add the dependency below:
```kotlin
dependencies {
implementation("com.github.skydoves:landscapist-zoomable:$version")
}
```
### ZoomablePlugin
You can implement zoom and pan gestures by adding `ZoomablePlugin` to your image component:
```kotlin
GlideImage( // CoilImage, FrescoImage also can be used.
imageModel = { poster.image },
component = rememberImageComponent {
+ZoomablePlugin()
}
)
```
### ZoomableState
You can create and remember a `ZoomableState` with `rememberZoomableState` to customize the zoom behavior and access the current transformation state:
```kotlin
val zoomableState = rememberZoomableState(
config = ZoomableConfig(
minZoom = 30f, // Minimum zoom scale
maxZoom = 4f, // Maximum zoom scale
doubleTapZoom = 20f, // Zoom scale on double-tap
enableDoubleTapZoom = true,
)
)
GlideImage( // or CoilImage
imageModel = { poster.image },
component = rememberImageComponent {
+ZoomablePlugin(state = zoomableState)
}
)
// Access current zoom state
val currentScale = zoomableState.transformation.scale
val currentOffset = zoomableState.transformation.offset
```
### Sub-Sampling
For very large images, Landscapist supports sub-sampling to efficiently display high-resolution images without running out of memory:
```kotlin
val zoomableState = rememberZoomableState(
config = ZoomableConfig(
enableSubSampling = true,
subSamplingConfig = SubSamplingConfig(
tileSize = 512.dp,
threshold = 2000.dp,
)
)
)
GlideImage( // or CoilImage
imageModel = { poster.image },
component = rememberImageComponent {
+ZoomablePlugin(state = zoomableState)
}
)
```
> **Note**: Sub-sampling is supported on Android (Glide, Coil3) and Kotlin Multiplatform (Coil3, targeting Android, iOS/macOS, and Destkop).
## BOM
[](https://central.sonatype.com/search?q=skydoves%2520landscapist)
The landscapist Bill of Materials (BOM) lets you manage all of your landscapist library versions by specifying only the BOM’s version.
```kotlin
dependencies {
// Import the landscapist BOM
implementation("com.github.skydoves:landscapist-bom:$version")
// Import landscapist libraries
implementation("com.github.skydoves:landscapist-glide") // fresco or coil
implementation("com.github.skydoves:landscapist-placeholder")
implementation("com.github.skydoves:landscapist-palette")
implementation("com.github.skydoves:landscapist-transformation")
implementation("com.github.skydoves:landscapist-zoomable")
}
```
## Taking Snapshot Images With Paparazzi
[Paparazzi](https://github.com/cashapp/paparazzi) allows you to take snapshot images of your Composable functions without running them on physical devices. You can take proper snapshots images about your images with Paparazzi like the below:
```kotlin
paparazzi.snapshot {
CompositionLocalProvider(LocalInspectionMode provides true) {
GlideImage(
modifier = Modifier.fillMaxSize(),
imageModel = { ".." },
previewPlaceholder = painterResource(R.drawable.placeholder)
)
}
}
```
## Who's using Landscapist?
If your project uses Landscapist, please let me know by creating a new issue! 🤗
## [Twitter for Android](https://user-images.githubusercontent.com/24237865/125583736-f0ffa76f-8f87-433b-a9fd-192231dc5e63.jpg)
[](https://play.google.com/store/apps/details?id=com.twitter.android&hl=ko&gl=US)
## Inspiration
This library was mostly inspired by [Accompanist](https://github.com/chrisbanes/accompanist).
> Accompanist is a group of libraries that contains some utilities which I've found myself copying around projects which use Jetpack Compose. Currently, it contains image loading and insets. You can get more variety and recent systems from the library maintained by Google.
## Find this repository useful? :heart:
Support it by joining __[stargazers](https://github.com/skydoves/Landscapist/stargazers)__ for this repository. :star:
Also __[follow](https://github.com/skydoves)__ me for my next creations! 🤩
# License
```xml
Designed and developed by 2020 skydoves (Jaewoong Eum)
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
http://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.
```


