[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7456":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":23,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":36,"readmeContent":37,"aiSummary":38,"trendingCount":16,"starSnapshotCount":16,"syncStatus":39,"lastSyncTime":40,"discoverSource":41},7456,"landscapist","skydoves\u002Flandscapist","skydoves","🌻 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:\u002F\u002Fskydoves.github.io\u002Flandscapist\u002F",null,"Kotlin",2549,135,12,5,0,1,7,59.6,"Apache License 2.0",false,"main",true,[25,26,27,28,29,30,31,32,33,5,34,35,7],"android","compose","compose-coil","compose-fresco","compose-glide","image","image-loading","jetpack-compose","kotlin","library","network","2026-06-12 04:00:33","![landscapist](https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F127760344-bb042fe8-23e1-4014-b208-b7b549d32086.png)\u003Cbr>\u003Cbr>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fdevlibrary.withgoogle.com\u002Fproducts\u002Fandroid\u002Frepos\u002Fskydoves-Landscapist\">\u003Cimg alt=\"Google\" src=\"https:\u002F\u002Fskydoves.github.io\u002Fbadges\u002Fgoogle-devlib.svg\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fyoutu.be\u002F8y65xBHDHK0?feature=shared\">\u003Cimg alt=\"Google\" src=\"https:\u002F\u002Fskydoves.github.io\u002Fbadges\u002Fyoutube-landscapist.svg\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fdoveletter\">\u003Cimg alt=\"Profile\" src=\"https:\u002F\u002Fskydoves.github.io\u002Fbadges\u002Fdove-letter.svg\"\u002F>\u003C\u002Fa>\u003Cbr>\n  \u003Ca href=\"https:\u002F\u002Fopensource.org\u002Flicenses\u002FApache-2.0\">\u003Cimg alt=\"License\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-Apache%202.0-blue.svg\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fandroid-arsenal.com\u002Fapi?level=21\">\u003Cimg alt=\"API\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FAPI-21%2B-brightgreen.svg?style=flat\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fskydoves\u002FLandscapist\u002Factions\">\u003Cimg[landscapist-core.md](docs\u002Flandscapist-core.md) alt=\"Build Status\" src=\"https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist\u002Fworkflows\u002FAndroid%20CI\u002Fbadge.svg\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fandroidweekly.net\u002Fissues\u002Fissue-441\">\u003Cimg alt=\"Android Weekly\" src=\"https:\u002F\u002Fskydoves.github.io\u002Fbadges\u002Fandroid-weekly.svg\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fskydoves.medium.com\u002Foptimized-image-loading-for-compose-and-kotlin-multiplatform-a45eb2e710c0\">\u003Cimg alt=\"Medium\" src=\"https:\u002F\u002Fskydoves.github.io\u002Fbadges\u002FStory-Medium.svg\"\u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fskydoves\">\u003Cimg alt=\"Profile\" src=\"https:\u002F\u002Fskydoves.github.io\u002Fbadges\u002Fskydoves.svg\"\u002F>\u003C\u002Fa> \n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n🌻 \u003Ca href=\"https:\u002F\u002Fskydoves.github.io\u002Flandscapist\" target=\"_blank\"> Landscapist\u003C\u002Fa> is a highly optimized, pluggable Jetpack Compose and Kotlin Multiplatform image loading solution that fetches and displays network images, and compatibles with \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fbumptech\u002Fglide\" target=\"_blank\"> Glide\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcoil-kt\u002Fcoil\" target=\"_blank\"> Coil\u003C\u002Fa>, and \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Ffresco\" target=\"_blank\"> Fresco.\u003C\u002Fa> 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. \u003Cbr>\u003Cbr> \u003Ca align=\"center\" href=\"https:\u002F\u002Fskydoves.github.io\u002Flandscapist\" target=\"_blank\">See official documentation for Landscapist\u003C\u002Fa>\n\u003C\u002Fp>\n\n## Who's using Landscapist?\n👉 [Check out who's using Landscapist](https:\u002F\u002Fskydoves.github.io\u002Flandscapist\u002F#whos-using-landscapist).\n\nLandscapist hits **+1,100,000 downloads every month** around the globe!\n\n![globe](https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F196018576-a9c87534-81a2-4618-8519-0024b67964bf.png)\n\n## Why Landscapist?\n\nLandscapist 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:\u002F\u002Fandroid-developers.googleblog.com\u002F2022\u002F01\u002Fimproving-app-performance-with-baseline.html) and it supports many pluggable features, such as [ImageOptions](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#imageoptions), [listening image state changes](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#listening-image-state-changes), [custom composables](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#custom-composables), [preview on Android Studio](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#preview-on-android-studio), [ImageComponent and ImagePlugin](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#imagecomponent-and-imageplugin), [placeholder](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#placeholder), [animations (circular reveal, crossfade)](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#animation), [transformation (blur)](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#transformation), [palette](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#palette), [zoomable](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#zoomable), and [image gallery](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist#image-gallery).\n\n\u003Cdetails>\n \u003Csummary>See the Compose compiler metrics for Landscapist\u003C\u002Fsummary>\n \n![metrics](https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F201906004-f4490bdf-7af9-4ad6-b586-7dcc6f07d0c8.png)\n\n\u003C\u002Fdetails>\n\n## Documentation\n\nFor comprehensive information about Landscapist, please refer to the **[official documentation](https:\u002F\u002Fskydoves.github.io\u002Flandscapist)**.\n\n## Technical Content\n\nFor 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:\u002F\u002Fmedium.com\u002Fproandroiddev\u002Foptimized-image-loading-for-compose-and-kotlin-multiplatform-a45eb2e710c0).\n\n## 💝 Sponsors\n\n\u003Ca href=\"https:\u002F\u002Fcoderabbit.link\u002FJaewoong\" target=\"_blank\"> \u003Cimg width=\"300\" alt=\"coderabbit\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F9823e1d3-8467-4d4d-8a53-94b3c0adc630\" \u002F>\u003C\u002Fa>\n\n\u003Ca href=\"https:\u002F\u002Fgetstream.io\u002Fchat\u002Fsdk\u002Fandroid\u002F?utm_source=github&utm_medium=referral&utm_content=&utm_campaign=Jaewoong_github_2025\" target=\"_blank\"> \u003Cimg width=\"260\" alt=\"stream\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F87a69228-4fef-4f48-ad98-1e2c606c5b7e\" \u002F>\u003C\u002Fa>\n\n## Demo projects\nYou can see the use cases of this library in the repositories below:\n- [google\u002Fmodernstorage](https:\u002F\u002Fgithub.com\u002Fgoogle\u002Fmodernstorage\u002Ftree\u002Fe62cda539ca75884dd49df3bcf8629751f0a91e6\u002Fsample): ModernStorage is a group of libraries that provide an abstraction layer over storage on Android to simplify its interactions.\n- [GetStream\u002FWhatsApp-Clone-Compose](https:\u002F\u002Fgithub.com\u002FgetStream\u002FwhatsApp-clone-compose): 📱 WhatsApp clone project demonstrates modern Android development built with Jetpack Compose and Stream Chat SDK for Compose.\n- [android\u002Fstorage-samples](https:\u002F\u002Fgithub.com\u002Fandroid\u002Fstorage-samples\u002Ftree\u002Fmain\u002FScopedStorage): Multiple samples showing the best practices in storage APIs on Android.\n- [skydoves\u002FDisneyCompose](https:\u002F\u002Fgithub.com\u002Fskydoves\u002Fdisneycompose): 🧸 A demo Disney app using Jetpack Compose and Hilt based on modern Android tech-stacks and MVVM architecture.\n- [skydoves\u002FMovieCompose](https:\u002F\u002Fgithub.com\u002Fskydoves\u002FMovieCompose): 🎞 A demo movie app using Jetpack Compose and Hilt based on modern Android tech stacks. \u003Cbr>\n\n## Landscapist Core & Image\n\nLandscapist 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:\n\n- **`landscapist-core`**: A standalone, Kotlin Multiplatform image loading engine with built-in memory\u002Fdisk caching, progressive loading, and network fetching via Ktor. Works on Android, iOS, Desktop, and Web without any UI dependencies.\n- **`landscapist-image`**: A Compose Multiplatform image component built on top of `landscapist-core` with full plugin support. Seamlessly works across all Compose Multiplatform targets.\n\nThese 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.\n\n\u003Cdiv class=\"header\">\n  \u003Ch1>Landscapist Core\u003C\u002Fh1>\n\u003C\u002Fdiv>\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\n\nThe `landscapist-core` module is a complete, Kotlin Multiplatform image loading solution that works standalone without any UI dependencies. It provides:\n\n- **Network image loading** via Ktor HTTP client.\n- **Memory caching** with LRU eviction and weak references.\n- **Disk caching** with size limits and automatic cleanup.\n- **Progressive loading** for better perceived performance.\n- **Priority-based scheduling** for optimized resource usage.\n- **Kotlin Multiplatform** support (Android, iOS, Desktop, Web).\n- **Image transformations** and custom decoders.\n- **Event listeners** for monitoring load states.\n\n### Why Choose Landscapist Core?\n\nLandscapist 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.\n\n**AAR Size Comparison (Android Release Build):**\n\n| Library | AAR Size | vs Landscapist Core | Impact on APK |\n|---------|----------|---------------------|---------------|\n| **landscapist-core** | **~312 KB** | **Baseline (Smallest)** | Minimal |\n| Coil3 | ~460 KB | **+47% larger** | Moderate |\n| Glide | ~689 KB | **+121% larger** | Significant |\n| Fresco | ~1 MB | **+228% larger** | High |\n\n**Performance (Android Release Build):**\n\n| Library | Avg Load Time | Memory Usage | Supports KMP |\n|---------|---------------|--------------|--------------|\n| **LandscapistImage** | **1,245ms** | **4,520KB** | **✓** |\n| **GlideImage** | 1,312ms (+5%) | 5,124KB (+13%) | ✗ |\n| **CoilImage** | 1,389ms (+12%) | 4,876KB (+8%) | **✓** |\n| **FrescoImage** | 1,467ms (+18%) | 5,342KB (+18%) | ✗ |\n\n> For detailed performance analysis and test methodology, see the [Landscapist Core documentation](https:\u002F\u002Fskydoves.github.io\u002Flandscapist\u002Flandscapist-core\u002F#performance-comparison).\n\n### Setup\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-core:$version\")\n}\n```\n\nFor Kotlin Multiplatform, add to your **module**'s `build.gradle.kts`:\n\n```kotlin\nsourceSets {\n    commonMain.dependencies {\n        implementation(\"com.github.skydoves:landscapist-core:$version\")\n    }\n}\n```\n\nAll platform-specific Ktor engines are included automatically based on your target platforms.\n\n### Using Landscapist Core for Network Loading\n\nYou 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.\n\n#### Android Example\n\nThis 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.\n\n```kotlin\nimport com.skydoves.landscapist.core.Landscapist\nimport com.skydoves.landscapist.core.LandscapistConfig\nimport com.skydoves.landscapist.core.ImageRequest\nimport kotlinx.coroutines.flow.collect\n\n\u002F\u002F Create a Landscapist instance (typically once in your app)\nval landscapist = Landscapist.builder(context)\n    .config(\n        LandscapistConfig(\n            memoryCacheSize = 64 * 1024 * 1024L, \u002F\u002F 64MB\n            diskCacheSize = 100 * 1024 * 1024L,   \u002F\u002F 100MB\n        )\n    )\n    .build()\n\n\u002F\u002F Load an image\nlifecycleScope.launch {\n    val request = ImageRequest.builder()\n        .model(\"https:\u002F\u002Fexample.com\u002Fimage.jpg\")\n        .size(width = 800, height = 600)\n        .build()\n\n    landscapist.load(request).collect { result ->\n        when (result) {\n            is ImageResult.Loading -> {\n                \u002F\u002F Show loading state\n            }\n            is ImageResult.Success -> {\n                val imageBitmap = result.data\n                \u002F\u002F Use the loaded ImageBitmap\n            }\n            is ImageResult.Failure -> {\n                \u002F\u002F Handle error\n            }\n        }\n    }\n}\n```\n\n#### Kotlin Multiplatform Example\n\nFor 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.\n\n```kotlin\nimport com.skydoves.landscapist.core.Landscapist\nimport com.skydoves.landscapist.core.ImageRequest\n\n\u002F\u002F Get the default instance (works on all platforms)\nval landscapist = Landscapist.getInstance()\n\nsuspend fun loadImage(url: String): ImageBitmap? {\n    val request = ImageRequest.builder()\n        .model(url)\n        .build()\n\n    var bitmap: ImageBitmap? = null\n    landscapist.load(request).collect { result ->\n        if (result is ImageResult.Success) {\n            bitmap = result.data\n        }\n    }\n    return bitmap\n}\n```\n\n### Advanced Configuration\n\nCustomize 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.\n\n```kotlin\nval landscapist = Landscapist.builder(context)\n    .config(\n        LandscapistConfig(\n            \u002F\u002F Memory cache\n            memoryCacheSize = 64 * 1024 * 1024L,\n\n            \u002F\u002F Disk cache\n            diskCacheSize = 100 * 1024 * 1024L,\n\n            \u002F\u002F Network settings\n            networkConfig = NetworkConfig(\n                connectTimeout = 10.seconds,\n                readTimeout = 30.seconds,\n                userAgent = \"MyApp\u002F1.0\"\n            ),\n\n            \u002F\u002F Performance optimizations\n            allowRgb565 = true,  \u002F\u002F Use less memory for images without transparency\n            weakReferencesEnabled = true,\n\n            \u002F\u002F Event listener\n            eventListenerFactory = EventListener.Factory { request ->\n                object : EventListener {\n                    override fun onStart(request: ImageRequest) {\n                        println(\"Started loading: ${request.model}\")\n                    }\n\n                    override fun onSuccess(request: ImageRequest, result: ImageResult.Success) {\n                        println(\"Loaded from: ${result.dataSource}\")\n                    }\n                }\n            }\n        )\n    )\n    .build()\n```\n\n\u003Cdiv class=\"header\">\n  \u003Ch1>Landscapist Image\u003C\u002Fh1>\n\u003C\u002Fdiv>\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\n\nThe `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).\n\n### Setup\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-image:$version\")\n}\n```\n\n> **Note**: This module depends on `landscapist-core`, which includes Ktor client automatically. No need to add Ktor dependencies separately.\n\nFor Kotlin Multiplatform:\n\n```kotlin\nsourceSets {\n    commonMain.dependencies {\n        implementation(\"com.github.skydoves:landscapist-image:$version\")\n    }\n}\n```\n\n### LandscapistImage\n\nLoad images in Compose using `LandscapistImage`:\n\n```kotlin\nimport com.skydoves.landscapist.image.LandscapistImage\nimport com.skydoves.landscapist.ImageOptions\n\nLandscapistImage(\n    imageModel = { \"https:\u002F\u002Fexample.com\u002Fimage.jpg\" },\n    modifier = Modifier.size(300.dp),\n    imageOptions = ImageOptions(\n        contentScale = ContentScale.Crop,\n        alignment = Alignment.Center\n    )\n)\n```\n\n### With Plugins\n\n`LandscapistImage` supports all Landscapist plugins:\n\n```kotlin\nimport com.skydoves.landscapist.components.rememberImageComponent\nimport com.skydoves.landscapist.placeholder.shimmer.ShimmerPlugin\nimport com.skydoves.landscapist.animation.crossfade.CrossfadePlugin\nimport com.skydoves.landscapist.transformation.blur.BlurTransformationPlugin\n\nLandscapistImage(\n    imageModel = { imageUrl },\n    modifier = Modifier.fillMaxWidth(),\n    component = rememberImageComponent {\n        +ShimmerPlugin(\n            baseColor = Color.Gray,\n            highlightColor = Color.LightGray\n        )\n        +CrossfadePlugin(duration = 550)\n        +BlurTransformationPlugin(radius = 10)\n    }\n)\n```\n\n### Custom Landscapist Instance\n\nProvide a custom `Landscapist` instance to your composition tree:\n\n```kotlin\nimport com.skydoves.landscapist.core.Landscapist\nimport com.skydoves.landscapist.image.LocalLandscapist\nimport androidx.compose.runtime.CompositionLocalProvider\n\n\u002F\u002F Create custom instance\nval customLandscapist = Landscapist.builder(context)\n    .config(\n        LandscapistConfig(\n            memoryCacheSize = 128 * 1024 * 1024L \u002F\u002F 128MB\n        )\n    )\n    .build()\n\n\u002F\u002F Provide to composition\nCompositionLocalProvider(LocalLandscapist provides customLandscapist) {\n    LandscapistImage(\n        imageModel = { imageUrl },\n        \u002F\u002F Will use the custom instance\n    )\n}\n```\n\n### Loading States\n\nHandle loading, success, and failure states:\n\n```kotlin\nLandscapistImage(\n    imageModel = { imageUrl },\n    loading = {\n        Box(modifier = Modifier.fillMaxSize()) {\n            CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))\n        }\n    },\n    success = { state, painter ->\n        Image(\n            painter = painter,\n            contentDescription = \"Loaded image\"\n        )\n    },\n    failure = {\n        Text(\"Failed to load image\")\n    }\n)\n```\n\n### Image State Changes\n\nMonitor state changes with a callback:\n\n```kotlin\nvar currentState by remember { mutableStateOf\u003CLandscapistImageState>(LandscapistImageState.None) }\n\nLandscapistImage(\n    imageModel = { imageUrl },\n    onImageStateChanged = { state ->\n        currentState = state\n    }\n)\n\nwhen (currentState) {\n    is LandscapistImageState.Loading -> { \u002F* loading *\u002F }\n    is LandscapistImageState.Success -> { \u002F* success *\u002F }\n    is LandscapistImageState.Failure -> { \u002F* failure *\u002F }\n    else -> { \u002F* none *\u002F }\n}\n```\n\n### Supported Image Sources\n\n`LandscapistImage` supports various image sources including network URLs, local files, drawable resources, and more. See the [Landscapist Image documentation](https:\u002F\u002Fskydoves.github.io\u002Flandscapist\u002Flandscapist-image\u002F#supported-image-sources) for a complete list of supported image sources per platform.\n\n---\n\n\u003Cdiv class=\"header\">\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fbumptech\u002Fglide\" target=\"_blank\"> \u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F95545537-1bc15200-0a39-11eb-883d-644f564da5d3.png\" align=\"left\" width=\"4%\" alt=\"Glide\" \u002F>\u003C\u002Fa>\n  \u003Ch1>Glide\u003C\u002Fh1>\n\u003C\u002Fdiv>\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nAdd the codes below to your **root** `build.gradle` file (not your module-level build.gradle file):\n```gradle\nallprojects {\n    repositories {\n        mavenCentral()\n    }\n}\n```\n\nNext, add the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-glide:2.9.7\")\n}\n```\n\n> **Note**: `Landscapist-Glide` includes version `4.16.0` of [Glide](https:\u002F\u002Fgithub.com\u002Fbumptech\u002Fglide) 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:\u002F\u002Fgithub.com\u002Fskydoves\u002FLandscapist\u002Freleases).\n\n### GlideImage\nYou can load images simply by using `GlideImage` composable function as the following example below:\n\n```kotlin\nGlideImage(\n  imageModel = { imageUrl }, \u002F\u002F loading a network image using an URL.\n  imageOptions = ImageOptions(\n    contentScale = ContentScale.Crop,\n    alignment = Alignment.Center\n  )\n)\n```\n\n### More Details for GlideImage\n\u003Cdetails>\n \u003Csummary>👉 Read further for more details\u003C\u002Fsummary>\n\n### Custom RequestOptions and TransitionOptions\nYou can customize your request-options with your own [RequestOptions](https:\u002F\u002Fbumptech.github.io\u002Fglide\u002Fdoc\u002Foptions.html#requestoptions) and [TransitionOptions](https:\u002F\u002Fbumptech.github.io\u002Fglide\u002Fdoc\u002Foptions.html#transitionoptions) for applying caching strategies, loading transformations like below:\n\n```kotlin\nGlideImage(\n  imageModel = { imageUrl },\n  requestOptions = {\n    RequestOptions()\n        .override(256, 256)\n        .diskCacheStrategy(DiskCacheStrategy.ALL)\n        .centerCrop()\n  }\n)\n```\n\n### Custom RequestBuilder\nYou can request image with your own [RequestBuilder](https:\u002F\u002Fbumptech.github.io\u002Fglide\u002Fdoc\u002Foptions.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.\n\n```kotlin\nGlideImage(\n  imageModel = { imageUrl },\n  requestBuilder = { Glide.with(LocalContext.current.applicationContext).asDrawable() },\n  modifier = Modifier.constrainAs(image) {\n    centerHorizontallyTo(parent)\n    top.linkTo(parent.top)\n  }.aspectRatio(0.8f)\n)\n```\n\n### Custom RequestListener\nYou can register your own [RequestListener](https:\u002F\u002Fbumptech.github.io\u002Fglide\u002Fjavadocs\u002F440\u002Fcom\u002Fbumptech\u002Fglide\u002Frequest\u002FRequestListener.html), which allows you to trace the status of a request while images load.\n\n```kotlin\nGlideImage(\n  imageModel = { imageUrl },\n  requestListener = object: RequestListener\u003CDrawable> {\n    override fun onLoadFailed(\n      e: GlideException?,\n      model: Any?,\n      target: Target\u003CDrawable>?,\n      isFirstResource: Boolean\n    ): Boolean {\n      \u002F\u002F do something\n      return false\n    }\n\n    override fun onResourceReady(\n      resource: Drawable?,\n      model: Any?,\n      target: Target\u003CDrawable>?,\n      dataSource: DataSource?,\n      isFirstResource: Boolean\n    ): Boolean {\n      \u002F\u002F do something\n      return true\n    }\n  }\n)\n```\n\n### LocalGlideRequestOptions\nYou can pass the same instance of your `RequestOptions` down through the Composition in your composable hierarchy as following the example below:\n\n```kotlin\nval requestOptions = RequestOptions()\n    .override(300, 300)\n    .circleCrop()\n\nCompositionLocalProvider(LocalGlideRequestOptions provides requestOptions) {\n  \u002F\u002F Loads images with the custom `requestOptions` without explicit defines.\n  GlideImage(\n    imageModel = ...\n  )\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdiv class=\"header\">\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcoil-kt\u002Fcoil\" target=\"_blank\"> \u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F95545538-1cf27f00-0a39-11eb-83dd-ef9b8c6a74cb.png\" align=\"left\" width=\"4%\" alt=\"Fresco\" \u002F>\u003C\u002Fa>\n  \u003Ch1>Coil\u003C\u002Fh1>\n\u003C\u002Fdiv>\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-coil:$version\")\n}\n```\n\nIf you're targeting on Kotlin Multiplatform, add the dependency below to your module's `build.gradle.kts` file:\n\n```kotlin\nsourceSets {\n    val commonMain by getting {\n        dependencies {\n            implementation(\"com.github.skydoves:landscapist-coil3:$version\")\n        }\n    }\n}\n```\n\nThe `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.\n\n> **Note**: Please make sure your project uses the same Jetpack Compose version on the [release page](https:\u002F\u002Fgithub.com\u002Fskydoves\u002FLandscapist\u002Freleases).\n\n### CoilImage\nYou can load images by using the `CoilImage` composable function as the following example below:\n\n```kotlin\nCoilImage(\n  imageModel = { imageUrl }, \u002F\u002F loading a network image or local resource using an URL.\n  imageOptions = ImageOptions(\n    contentScale = ContentScale.Crop,\n    alignment = Alignment.Center\n  )\n)\n```\n\n### More Details for CoilImage\n\n\u003Cdetails>\n \u003Csummary>👉 Read further for more details\u003C\u002Fsummary>\n\n### Custom ImageRequest and ImageLoader\nYou can load images with your own [ImageRequest](https:\u002F\u002Fcoil-kt.github.io\u002Fcoil\u002Fimage_requests\u002F) and [ImageLoader](https:\u002F\u002Fcoil-kt.github.io\u002Fcoil\u002Fimage_loaders\u002F), which provides all the necessary information for loading images like caching strategies and transformations.\n\n```kotlin\nCoilImage(\n  imageRequest = {\n      ImageRequest.Builder(LocalContext.current)\n        .data(poster.poster)\n        .crossfade(true)\n        .build() },\n  imageLoader = {\n      ImageLoader.Builder(LocalContext.current)\n        .availableMemoryPercentage(0.25)\n        .crossfade(true)\n        .build() },\n  modifier = modifier,\n)\n```\n\n ### LocalCoilImageLoader\n You can pass the same instance of your `ImageLoader` down through the Composition in your composable hierarchy as following the example below:\n\n ```kotlin\n val imageLoader = ImageLoader.Builder(context).build()\nCompositionLocalProvider(LocalCoilImageLoader provides imageLoader) {\n  \n   \u002F\u002F This will automatically use the value of current imageLoader in the hierarchy.\n   CoilImage(\n     imageModel = ...\n   )\n }\n ```\n\n \u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F131246748-b88903a1-43de-4e6c-9069-3e956a0cf8a6.gif\" align=\"right\" width=\"32%\"\u002F>\n\n## Animated Image Supports (GIF, Webp)\nYou can load animated GIFs and WebP Images with your `ImageLoader`.\n\n```kotlin\nval context = LocalContext.current\nval imageLoader = ImageLoader.Builder(context)\n  .components {\n    if (SDK_INT >= 28) {\n      add(ImageDecoderDecoder.Factory())\n    } else {\n      add(GifDecoder.Factory())\n    }\n  }\n  .build()\n\nCoilImage(\n    imageModel = { poster.gif }, \u002F\u002F URL of an animated image.\n    imageLoader = { imageLoader },\n    shimmerParams = ShimmerParams(\n      baseColor = background800,\n      highlightColor = shimmerHighLight\n    ),\n    modifier = Modifier\n      .fillMaxWidth()\n      .padding(8.dp)\n      .height(500.dp)\n      .clip(RoundedCornerShape(8.dp))\n  )\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdiv class=\"header\">\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Ffresco\" target=\"_blank\"> \u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F95545540-1cf27f00-0a39-11eb-9e84-96b9df81364b.png\" align=\"left\" width=\"4%\" alt=\"Fresco\" \u002F>\u003C\u002Fa>\n  \u003Ch1>Fresco\u003C\u002Fh1>\n\u003C\u002Fdiv>\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n```gradle\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-fresco:$version\")\n}\n```\n> **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:\u002F\u002Fgithub.com\u002Fskydoves\u002FLandscapist\u002Freleases).\n\n### Setup\nTo get started, you should set up `Fresco` with [ImagePipelineConfig](https:\u002F\u002Ffrescolib.org\u002Fdocs\u002Fconfigure-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:\u002F\u002Ffrescolib.org\u002Fdocs\u002Fusing-other-network-layers.html#using-okhttp).\n```kotlin\nclass App : Application() {\n\n  override fun onCreate() {\n    super.onCreate()\n\n    val pipelineConfig =\n      OkHttpImagePipelineConfigFactory\n        .newBuilder(this, OkHttpClient.Builder().build())\n        .setDiskCacheEnabled(true)\n        .setDownsampleEnabled(true)\n        .setResizeAndRotateEnabledForNetwork(true)\n        .build()\n\n    Fresco.initialize(this, pipelineConfig)\n  }\n}\n```\n\n### FrescoImage\nYou can load images by using the `FrescoImage` composable function as the following example below:\n\n```kotlin\nFrescoImage(\n  imageUrl = stringImageUrl, \u002F\u002F loading a network image using an URL.\n  imageOptions = ImageOptions(\n    contentScale = ContentScale.Crop,\n    alignment = Alignment.Center\n  )\n)\n```\n\n### More Details for FrescoImage\n\u003Cdetails>\n \u003Csummary>👉 Read further for more details\u003C\u002Fsummary>\n\n### Custom ImageRequest\nYou can load images with your own [ImageRequest](https:\u002F\u002Ffrescolib.org\u002Fdocs\u002Fimage-requests.html), which provides some necessary information for loading images like decoding strategies and resizing.\n\n```kotlin\nval imageRequest = ImageRequestBuilder\n  .newBuilderWithSource(uri)\n  .setImageDecodeOptions(decodeOptions)\n  .setLocalThumbnailPreviewsEnabled(true)\n  .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)\n  .setProgressiveRenderingEnabled(false)\n  .setResizeOptions(ResizeOptions(width, height))\n  .build()\n\nFrescoImage(\n  imageUrl = stringImageUrl,\n  imageRequest = { imageRequest }\n)\n```\n\n### LocalFrescoImageRequest\nYou can pass the same instance of your `imageRequestBuilder` down through the Composition in your composable hierarchy as following the example below:\n\n```kotlin\n\u002F\u002F customize the ImageRequest as needed\nval imageRequestBuilder = ImageRequestBuilder\n  .newBuilderWithSource(uri)\n  .setImageDecodeOptions(decodeOptions)\n  .setLocalThumbnailPreviewsEnabled(true)\n  .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)\n  .setProgressiveRenderingEnabled(false)\n  .setResizeOptions(ResizeOptions(width, height))\n\nCompositionLocalProvider(LocalFrescoImageRequest provides imageRequestBuilder) {\n  \u002F\u002F This will automatically use the value of current ImageRequest in the hierarchy.\n  FrescoImage(\n    imageUrl = ...\n  )\n}\n```\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F131246748-b88903a1-43de-4e6c-9069-3e956a0cf8a6.gif\" align=\"right\" width=\"32%\"\u002F>\n\n## Fresco Animated Image Support (GIF, Webp)\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nAdd the below dependency to your **module**'s `build.gradle` file.\n\n```gradle\ndependencies {\n  implementation(\"com.github.skydoves:landscapist-fresco-websupport:$version\")\n}\n```\n\nYou can load animated GIFs and WebP Images with `FrescoWebImage` composable function. You should pass the `AbstractDraweeController` like the following example below:\n\n```kotlin\nFrescoWebImage(\n  controllerBuilder = {\n      Fresco.newDraweeControllerBuilder()\n          .setUri(poster.gif) \u002F\u002F GIF or Webp image url.\n          .setAutoPlayAnimations(true)\n  },\n  modifier = Modifier\n    .fillMaxWidth()\n    .height(300.dp)\n)\n```\n\nFor more details, check out [DraweeController](https:\u002F\u002Ffrescolib.org\u002Fdocs\u002Fanimations.html), and [Supported URIs](https:\u002F\u002Ffrescolib.org\u002Fdocs\u002Fsupported-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.\n\n\u003C\u002Fdetails>\n\n## ImageOptions\n\nYou can give image options to your image composable functions by passing `ImageOptions` instance like the below:\n\n```kotlin\nGlideImage(\n  ..\n  imageOptions = ImageOptions(\n      contentScale = ContentScale.Crop,\n      alignment = Alignment.Center,\n      contentDescription = \"main image\",\n      colorFilter = null,\n      alpha = 1f\n    )\n)\n```\n\n### RequestSize\n\nYou can set the request size of your image by giving `requestSize` property as seen in the below:\n\n```kotlin\nGlideImage(\n  ..\n  imageOptions = ImageOptions(requestSize = IntSize(800, 600)),\n)\n```\n\n## Listening image state changes\n\nYou can listen the image state changes by giving `onImageStateChanged` parameter to your image composable functions like the below:\n\n```kotlin\nGlideImage(\n  ..\n  onImageStateChanged = {\n    when (it) {\n      GlideImageState.None -> ..\n      GlideImageState.Loading -> ..\n      is GlideImageState.Success -> ..\n      is GlideImageState.Failure -> ..\n    }\n  }\n)\n```\n\n> **Note**: You can use `CoilImageState` for `CoilImage` and `FrescoImageState` for `FrescoImage`.\n\n### DataSource\n\nFor the success state, you can trace the origin of the image with the `DataSource` parameter. `DataSource` represents the following source origins below:\n\n- **Memory**: Represents an in-memory data source or cache (e.g. bitmap, ByteBuffer).\n- **Disk**: Represents a disk-based data source (e.g. drawable resource, or File).\n- **Network**: Represents a network-based data source.\n- **Unknown**: Represents an unknown data source.\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F94174882-d6e1db00-fed0-11ea-86ec-671b5039b1b9.gif\" align=\"right\" width=\"310px\"\u002F>\n\n## Custom Composables\nYou can execute your own composable functions depending on the three request states below:\n\n- **loading**: Executed while loading an image.\n- **success**: Executed if loading an image successfully.\n- **failure**: Executed if fails to load an image (e.g. network error, wrong destination).\n\n```kotlin\nGlideImage( \u002F\u002F CoilImage, FrescoImage\n  imageModel = { imageUrl },\n  modifier = modifier,\n  \u002F\u002F shows an indicator while loading an image.\n  loading = {\n    Box(modifier = Modifier.matchParentSize()) {\n      CircularProgressIndicator(\n        modifier = Modifier.align(Alignment.Center)\n      )\n    }\n  },\n  \u002F\u002F shows an error text if fail to load an image.\n  failure = {\n    Text(text = \"image request failed.\")\n  }\n)\n```\n\nAlso, you can customize the image content with our own composable function like the example below:\n\n```kotlin\nGlideImage( \u002F\u002F CoilImage, FrescoImage\n  imageModel = { imageUrl },\n  success = { state, painter ->\n    Image(\n      painter = painter,\n      modifier = Modifier.size(128.dp), \u002F\u002F draw a resized image.\n      contentDescription = \"Image\"\n    )\n  },\n  ..  \n)\n```\n> **Note**: You can also use the custom Composables for **`CoilImage`** and **`FrescoImage`**.\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F148672035-6a82eba5-900c-44ee-a42c-acbf8038d0ab.png\" align=\"right\" width=\"46%\">\n\n\n## Preview on Android Studio\nLandscapist 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:\n\n```kotlin\nGlideImage(\n  imageModel = { imageUrl },\n  modifier = Modifier.aspectRatio(0.8f),\n  previewPlaceholder = painterResource(id = R.drawable.poster)\n)\n```\n> **Note**: You can also use the the `previewPlaceholder` parameter for **`CoilImage`** and **`FrescoImage`**.\n\n## ImageComponent and ImagePlugin\n\nYou can compose supported image plugins by Landscapist or you can create your own image plugin that will be composed following the image loading state.\n`ImagePlugin` is a pluggable compose interface that will be executed for loading images. `ImagePlugin` provides following types below:\n\n- **PainterPlugin**: A pinter plugin interface to be composed with the given `Painter`.\n- **LoadingStatePlugin**: A pluggable state plugin that will be composed while the state is `ImageLoadState.Loading`.\n- **SuccessStatePlugin**: A pluggable state plugin that will be composed when the state is `ImageLoadState.Success`.\n- **FailureStatePlugin**: A pluggable state plugin that will be composed when the state is `ImageLoadState.Failure`.\n\nFor example, you can implement your own `LoadingStatePlugin` that will be composed while loading an image like the below:\n\n```kotlin\ndata class LoadingPlugin(val source: Any?) : ImagePlugin.LoadingStatePlugin {\n\n  @Composable\n  override fun compose(\n    modifier: Modifier,\n    imageOptions: ImageOptions?\n  ): ImagePlugin = apply {\n    if (source != null && imageOptions != null) {\n      ImageBySource(\n        source = source,\n        modifier = modifier,\n        alignment = imageOptions.alignment,\n        contentDescription = imageOptions.contentDescription,\n        contentScale = imageOptions.contentScale,\n        colorFilter = imageOptions.colorFilter,\n        alpha = imageOptions.alpha\n      )\n    }\n  }\n}\n```\n\nNext, you can compose plugins by adding them in the `rememberImageComponent` like the below:\n\n```kotlin\nGlideImage(\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    add(CircularRevealPlugin())\n    add(LoadingPlugin(source))\n  },\n)\n```\n\nor you can just add plugins by using the **+** expression like the below:\n\n```kotlin\nGlideImage(\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +CircularRevealPlugin()\n    +LoadingPlugin(source)\n  },\n)\n```\n\n### LocalImageComponent\n\nYou can provide the same `ImageComponent` instance in the composable hierarchy by using `imageComponent` extension and `LocalImageComponent` like the below:\n\n```kotlin\nval component = imageComponent {\n  +CrossfadePlugin()\n  +PalettePlugin()\n}\n\nCompositionLocalProvider(LocalImageComponent provides component) {\n  ..\n}\n```\n\n## Placeholder\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nThe `landscapist-placeholder` package provides useful image plugins, such as loading & failure placeholder supports and shimmering animation.\nTo use placeholder supports, add the dependency below:\n\n```kotlin\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-placeholder:$version\")\n}\n```\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F95812167-be3a4780-0d4f-11eb-9360-2a4a66a3fb46.gif\" align=\"right\" width=\"250px\"\u002F>\n\n### ShimmerPlugin\nYou can implement a shimmering effect while loading an image by using the `ShimmerPlugin` as following the example below:\n\n```kotlin\nGlideImage( \u002F\u002F CoilImage, FrescoImage\n  imageModel = { imageUrl },\n  modifier = modifier,\n  component = rememberImageComponent {\n    \u002F\u002F shows a shimmering effect when loading an image.\n    +ShimmerPlugin(\n      Shimmer.Flash(\n        baseColor = Color.White,\n        highlightColor = Color.LightGray,\n      ),\n    )\n  },\n  \u002F\u002F shows an error text message when request failed.\n  failure = {\n    Text(text = \"image request failed.\")\n  }\n)\n ```\n > **Note**: You can also use the Shimmer effect for **`CoilImage`** and **`FrescoImage`**.\n\n`Shimmer` sealed class provides following the three different types: `Resonate`, `Fade`, and `Flash`.\n\n|                                                               Resonate                                                                |                                                                 Fade                                                                  |                                                                 Flash                                                                 |\n|:-------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------:|\n| \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist\u002Fassets\u002F24237865\u002Fbf8f6d04-2e30-44a5-ba9d-9e706af15a09\" align=\"center\" width=\"100%\"\u002F> | \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist\u002Fassets\u002F24237865\u002Fadd42855-9e71-4222-b41e-aa9cfa7f0ce3\" align=\"center\" width=\"100%\"\u002F> | \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist\u002Fassets\u002F24237865\u002Fc79bcad3-bd30-4b63-b3f8-5b3bdd7c561a\" align=\"center\" width=\"100%\"\u002F> |\n\n### PlaceholderPlugin\n\nYou can show your own placeholder while loading an image or when fails to load an image with `PlaceholderPlugin.Loading` and `PlaceholderPlugin.Failure`.\n\n```kotlin\nGlideImage(\n  ..\n  component = rememberImageComponent {\n      +PlaceholderPlugin.Loading(painterResource(id = R.drawable.placeholder_loading))\n      +PlaceholderPlugin.Failure(painterResource(id = R.drawable.placeholder_failure))\n    },\n)\n```\n\n> **Note**: The source should be one of `ImageBitmap`, `ImageVector`, or `Painter`.\n\n### ThumbnailPlugin\n\n\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fskydoves\u002Flandscapist\u002Fassets\u002F24237865\u002Fdad9db76-31c5-453a-98a8-f3dfd3103993\" align=\"right\" width=\"250px\" \u002F>\n\nLandscapist 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.\nTo show thumbnail, add the image plugin into your image component like the example below:\n\n```kotlin\nGlideImage(\n  ..,\n  component = rememberImageComponent {\n      +ThumbnailPlugin() \n  },\n)\n```\n\nYou can also adjust the request sizes by giving the `requestSize` parameter:\n\n```kotlin\ncomponent = rememberImageComponent {\n    +ThumbnailPlugin(IntSize(30 ,30)) \n},\n```\n\n> **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.\n\n\n## Animation\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nThe `landscapist-animation` package provides useful image plugins related to animations, such as crossfade and circular reveal animation.\nTo use animation supports, add the dependency below:\n\n```kotlin\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-animation:$version\")\n}\n```\n\n### Preview\n\n|                                                                Circular Reveal                                                                 |                                                                   Crossfade                                                                    |\n|:----------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------:|\n| \u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F189552544-5f8e1209-4930-45e6-a050-3a0cda088e9f.gif\" align=\"center\" width=\"100%\"\u002F> | \u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F189552547-d933cee7-e811-4170-a806-1ac165e8f055.gif\" align=\"center\" width=\"100%\"\u002F> | \n\n\n### Crossfade Animation\n\nYou can implement the crossfade animation while drawing images with `CrossfadePlugin` as the following:\n\n```kotlin\nGlideImage(\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +CrossfadePlugin(\n      duration = 550\n    )\n  }\n)\n```\n\n > **Note**: You can also use the crossfade animation for **`CoilImage`** and **`FrescoImage`**.\n\n### Circular Reveal Animation\nYou can implement the circular reveal animation while drawing images with `CircularRevealplugin` as the following:\n\n```kotlin\nGlideImage(\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +CircularRevealPlugin(\n      duration = 350\n    )\n  }\n)\n```\n\n > **Note**: You can also use the Circular Reveal animation for **`CoilImage`** and **`FrescoImage`**.\n\n ## Transformation\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F196038507-54a3a79c-2e8e-45ec-b5e8-5de65cd50248.png\" align=\"right\" width=\"250\"\u002F>\n\nThe `landscapist-transformation` package provides useful image transformation plugins, such as the blur effect.\nTo use transformation supports, add the dependency below:\n\n```kotlin\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-transformation:$version\")\n}\n```\n\n### BlurTransformationPlugin\n\nYou can implement the blur effect with `BlurTransformationPlugin` as the following:\n\n```kotlin\nGlideImage( \u002F\u002F CoilImage, FrescoImage also can be used.\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n      +BlurTransformationPlugin(radius = 10) \u002F\u002F between 0 to Int.MAX_VALUE.\n  }\n)\n```\n\n>**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.\n\n## Palette\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\nThe `landscapist-palette` package provides useful image plugins related to palette, such as extracting primary color sets.\nTo use palette supports, add the dependency below:\n\n```kotlin\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-palette:$version\")\n}\n```\n\nYou can extract primary (theme) color profiles with `PalettePlugin`. You can check out [Extract color profiles](https:\u002F\u002Fdeveloper.android.com\u002Ftraining\u002Fmaterial\u002Fpalette-colors#extract-color-profiles) to see what kinds of colors can be extracted.\n\n\u003Cimg src=\"https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F129226361-877689b8-a1ec-4f59-b8a6-e2efe33a8de7.gif\" align=\"right\" width=\"250\"\u002F>\n\n```kotlin\nvar palette by rememberPaletteState(null)\n\nGlideImage( \u002F\u002F CoilImage, FrescoImage also can be used.\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n      +PalettePlugin { palette = it }\n  }\n)\n\nCrossfade(\n  targetState = palette,\n  modifier = Modifier\n    .padding(horizontal = 8.dp)\n    .size(45.dp)\n) {\n  Box(\n    modifier = Modifier\n      .background(color = Color(it?.lightVibrantSwatch?.rgb ?: 0))\n      .fillMaxSize()\n  )\n}\n```\n\nAlso, you can customize attributes of `PalettePlugin` like the example below:\n\n```kotlin\nvar palette by remember { mutableStateOf\u003CPalette?>(null) }\n\nGlideImage( \u002F\u002F CoilImage, FrescoImage also can be used.\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +PalettePlugin(\n      imageModel = poster.image,\n      useCache = true, \u002F\u002F use cache strategies for the same image model.\n      interceptor = {\n        it.addFilter { rgb, hsl ->\n          \u002F\u002F here edit to add the filter colors.\n          false\n        }\n      },\n      paletteLoadedListener = {\n        palette = it\n      }\n    )\n  }\n)\n```\n > **Note**: You can also use the Palette for **`CoilImage`** and **`FrescoImage`**.\n\n## Zoomable\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\n\u003Cimg src=\"preview\u002Fsample0.gif\" align=\"right\" width=\"32%\"\u002F>\n\nThe `landscapist-zoomable` package provides a `ZoomablePlugin` that enables zoom and pan gestures for images. This plugin supports both Android and Kotlin Multiplatform (iOS, Desktop).\n\nTo use zoomable supports, add the dependency below:\n\n```kotlin\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-zoomable:$version\")\n}\n```\n\n### ZoomablePlugin\n\nYou can implement zoom and pan gestures by adding `ZoomablePlugin` to your image component:\n\n```kotlin\nGlideImage( \u002F\u002F CoilImage, FrescoImage also can be used.\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +ZoomablePlugin()\n  }\n)\n```\n\n### ZoomableState\n\nYou can create and remember a `ZoomableState` with `rememberZoomableState` to customize the zoom behavior and access the current transformation state:\n\n```kotlin\nval zoomableState = rememberZoomableState(\n  config = ZoomableConfig(\n    minZoom = 30f,           \u002F\u002F Minimum zoom scale\n    maxZoom = 4f,           \u002F\u002F Maximum zoom scale\n    doubleTapZoom = 20f,     \u002F\u002F Zoom scale on double-tap\n    enableDoubleTapZoom = true,\n  )\n)\n\nGlideImage( \u002F\u002F or CoilImage\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +ZoomablePlugin(state = zoomableState)\n  }\n)\n\n\u002F\u002F Access current zoom state\nval currentScale = zoomableState.transformation.scale\nval currentOffset = zoomableState.transformation.offset\n```\n\n### Sub-Sampling\n\nFor very large images, Landscapist supports sub-sampling to efficiently display high-resolution images without running out of memory:\n\n```kotlin\nval zoomableState = rememberZoomableState(\n  config = ZoomableConfig(\n    enableSubSampling = true,\n    subSamplingConfig = SubSamplingConfig(\n      tileSize = 512.dp,\n      threshold = 2000.dp,\n    )\n  )\n)\n\nGlideImage( \u002F\u002F or CoilImage\n  imageModel = { poster.image },\n  component = rememberImageComponent {\n    +ZoomablePlugin(state = zoomableState)\n  }\n)\n```\n\n > **Note**: Sub-sampling is supported on Android (Glide, Coil3) and Kotlin Multiplatform (Coil3, targeting Android, iOS\u002FmacOS, and Destkop).\n\n## Image Gallery\n\n[![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\n\u003Cimg src=\"preview\u002Fgallery.gif\" align=\"right\" width=\"32%\"\u002F>\n\nThe `landscapist-image-gallery` package provides high level, Compose Multiplatform UI components for building photo gallery experiences on top of Landscapist:\n\n- **`ImageGallery`**: a thumbnail grid built on `LazyVerticalGrid` with optional multi select, header and footer slots, and a custom content slot.\n- **`ImageViewer`**: a full screen pager with pinch to zoom, swipe to dismiss, and top bar, bottom bar, and page indicator overlays.\n- **`ImageSharedTransitionConfig`**: drop in shared element transition between gallery thumbnails and viewer pages.\n\nBy default, both components render their images using `LandscapistImage`, so they work on Android, iOS, Desktop, and Web without extra setup. You can still swap in Glide, Coil, Fresco, or any custom loader through the `content` slot.\n\nTo use the gallery components, add the dependency below:\n\n```kotlin\ndependencies {\n    implementation(\"com.github.skydoves:landscapist-image-gallery:$version\")\n}\n```\n\nFor Kotlin Multiplatform:\n\n```kotlin\nkotlin {\n    sourceSets {\n        commonMain.dependencies {\n            implementation(\"com.github.skydoves:landscapist-image-gallery:$version\")\n        }\n    }\n}\n```\n\n### ImageGallery\n\n`ImageGallery` displays a list of image models in a `LazyVerticalGrid`. The items can be anything accepted by Landscapist (URL strings, `Uri`, `File`, `ByteArray`, drawable resources, and so on).\n\n```kotlin\nimport com.skydoves.landscapist.gallery.ImageGallery\n\nImageGallery(\n  images = imageUrls,\n  columns = GridCells.Fixed(3),\n  imageOptions = ImageOptions(contentScale = ContentScale.Crop),\n  component = rememberImageComponent {\n    +ShimmerPlugin(\n      shimmer = Shimmer.Resonate(\n        baseColor = Color.DarkGray,\n        highlightColor = Color.LightGray,\n      ),\n    )\n  },\n  onImageClick = { index, imageModel ->\n    \u002F\u002F open ImageViewer at `index`\n  },\n)\n```\n\nBecause each tile is rendered by `LandscapistImage` under the hood, the full plugin ecosystem (Shimmer, Crossfade, Palette, and so on) is available through `component`. Use the `content` lambda to render tiles with another loader (Glide, Coil, Fresco).\n\n#### Selection mode\n\nEnable multi select by flipping `selectable` and tracking the selected indices yourself. Long pressing any tile enters selection mode, tapping tiles while in selection mode toggles them, and `onImageClick` is **not** fired while a selection is active.\n\n```kotlin\nvar selectedIndices by remember { mutableStateOf(emptySet\u003CInt>()) }\n\nImageGallery(\n  images = imageUrls,\n  selectable = true,\n  selectedIndices = selectedIndices,\n  onSelectionChanged = { selectedIndices = it },\n  selectionOverlay = { _, selected ->\n    if (selected) {\n      Box(\n        modifier = Modifier\n          .fillMaxSize()\n          .background(Color.Black.copy(alpha = 0.3f)),\n        contentAlignment = Alignment.TopEnd,\n      ) {\n        Icon(\n          imageVector = Icons.Default.CheckCircle,\n          contentDescription = null,\n          modifier = Modifier.padding(6.dp).size(24.dp).clip(CircleShape),\n        )\n      }\n    }\n  },\n  header = { TopAppBar(title = { Text(\"Gallery\") }) },\n)\n```\n\n### ImageViewer\n\n`ImageViewer` is a full screen pager for viewing images one at a time. It includes horizontal paging, pinch and double tap zoom via `ZoomablePlugin`, swipe to dismiss, and slots for a top bar, bottom bar, and page indicator.\n\n```kotlin\nimport com.skydoves.landscapist.gallery.ImageViewer\nimport com.skydoves.landscapist.gallery.rememberImageViewerState\n\nval viewerState = rememberImageViewerState(\n  initialPage = startIndex,\n  pageCount = { imageUrls.size },\n)\n\nImageViewer(\n  images = imageUrls,\n  state = viewerState,\n  zoomableConfig = ZoomableConfig(\n    minZoom = 1f,\n    maxZoom = 5f,\n    doubleTapZoom = 2f,\n  ),\n  onDismiss = { navController.popBackStack() },\n  topBar = { current, total ->\n    TopAppBar(\n      backgroundColor = Color.Black.copy(alpha = 0.5f),\n      title = { Text(\"${current + 1} \u002F $total\", color = Color.White) },\n    )\n  },\n)\n```\n\nSwipe to dismiss is automatically disabled while the current page is zoomed, so zoom and pan gestures are not interrupted. Use the `content` slot to render pages with another loader while keeping zoom, paging, and dismiss behavior intact.\n\n### Shared element transition\n\nPass the **same** `ImageSharedTransitionConfig` to both `ImageGallery` and `ImageViewer` to animate a tapped thumbnail into the full screen viewer and back on dismiss.\n\n```kotlin\nSharedTransitionLayout {\n  AnimatedContent(\n    targetState = showViewer,\n    transitionSpec = { fadeIn() togetherWith fadeOut() },\n    label = \"gallery-viewer\",\n  ) { viewerVisible ->\n    val animatedContentScope = this\n    if (!viewerVisible) {\n      ImageGallery(\n        images = imageUrls,\n        onImageClick = { index, _ ->\n          selectedPage = index\n          showViewer = true\n        },\n        sharedTransition = ImageSharedTransitionConfig(\n          sharedTransitionScope = this@SharedTransitionLayout,\n          animatedContentScope = animatedContentScope,\n        ),\n      )\n    } else {\n      ImageViewer(\n        images = imageUrls,\n        state = rememberImageViewerState(\n          initialPage = selectedPage,\n          pageCount = { imageUrls.size },\n        ),\n        onDismiss = { showViewer = false },\n        sharedTransition = ImageSharedTransitionConfig(\n          sharedTransitionScope = this@SharedTransitionLayout,\n          animatedContentScope = animatedContentScope,\n        ),\n      )\n    }\n  }\n}\n```\n\nThe same pattern works with `NavHost`. Use the `AnimatedContentScope` from each `composable { ... }` destination as the `animatedContentScope`. Override `keyProvider` when gallery indices don't match viewer pages, when the same model appears multiple times, or when you need to namespace keys across multiple galleries.\n\n> For the complete API reference (state holders, defaults, Kotlin Multiplatform targets, all overlay slots), see the [Image Gallery documentation](https:\u002F\u002Fskydoves.github.io\u002Flandscapist\u002Fgallery\u002F).\n\n ## BOM\n\n [![Maven Central](https:\u002F\u002Fimg.shields.io\u002Fmaven-central\u002Fv\u002Fcom.github.skydoves\u002Flandscapist.svg?label=Maven%20Central)](https:\u002F\u002Fcentral.sonatype.com\u002Fsearch?q=skydoves%2520landscapist)\u003Cbr>\n\n The landscapist Bill of Materials (BOM) lets you manage all of your landscapist library versions by specifying only the BOM’s version.\n\n ```kotlin\ndependencies {\n    \u002F\u002F Import the landscapist BOM\n    implementation(\"com.github.skydoves:landscapist-bom:$version\")\n\n    \u002F\u002F Import landscapist libraries\n    implementation(\"com.github.skydoves:landscapist-glide\") \u002F\u002F fresco or coil\n    implementation(\"com.github.skydoves:landscapist-placeholder\")\n    implementation(\"com.github.skydoves:landscapist-palette\")\n    implementation(\"com.github.skydoves:landscapist-transformation\")\n    implementation(\"com.github.skydoves:landscapist-zoomable\")\n}\n ```\n\n ## Taking Snapshot Images With Paparazzi\n\n[Paparazzi](https:\u002F\u002Fgithub.com\u002Fcashapp\u002Fpaparazzi) 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:\n\n```kotlin\npaparazzi.snapshot {\n  CompositionLocalProvider(LocalInspectionMode provides true) {\n    GlideImage(\n      modifier = Modifier.fillMaxSize(),\n      imageModel = { \"..\" },\n      previewPlaceholder = painterResource(R.drawable.placeholder)\n  )\n }\n}\n```\n\n## Who's using Landscapist?\nIf your project uses Landscapist, please let me know by creating a new issue! 🤗\n\n## [Twitter for Android](https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F125583736-f0ffa76f-8f87-433b-a9fd-192231dc5e63.jpg)\n\n[![twitter](https:\u002F\u002Fuser-images.githubusercontent.com\u002F24237865\u002F125583182-9527dd48-433e-4e17-ae52-3f2bb544a847.jpg)](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.twitter.android&hl=ko&gl=US)\n\n## Inspiration\nThis library was mostly inspired by [Accompanist](https:\u002F\u002Fgithub.com\u002Fchrisbanes\u002Faccompanist).\u003Cbr>\n\n> 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.\n\n## Find this repository useful? :heart:\nSupport it by joining __[stargazers](https:\u002F\u002Fgithub.com\u002Fskydoves\u002FLandscapist\u002Fstargazers)__ for this repository. :star: \u003Cbr>\nAlso __[follow](https:\u002F\u002Fgithub.com\u002Fskydoves)__ me for my next creations! 🤩\n\n# License\n```xml\nDesigned and developed by 2020 skydoves (Jaewoong Eum)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","Landscapist 是一个高度优化的 Jetpack Compose 和 Kotlin 多平台图像加载库，用于从网络获取并显示图片，并兼容 Glide、Coil 和 Fresco。其核心功能包括支持追踪图像加载状态、自定义实现以及多种动画效果（如渐变、模糊变换和圆形揭示）。该库还允许用户通过插件轻松配置和附加图像加载行为。Landscapist 适用于需要高性能图像加载的 Android 应用开发场景，特别是在使用 Jetpack Compose 构建 UI 时，能够提供流畅且高效的用户体验。",2,"2026-06-11 03:12:33","top_language"]