Compose ImageLoader

repository·master·Indexed 19 days ago

https://github.com/qdsfdhvh/compose-imageloader

A Kotlin Multiplatform library for loading and displaying images in Compose applications across Android, iOS, JVM, and other platforms. It provides high-level components like AutoSizeImage and AutoSizeBox, as well as a standard rememberImagePainter approach. The library includes configurable memory and disk caching, support for various resource decoders (Compose Multiplatform, Moko, ImageIO), and a Blur Interceptor for bitmaps.

Tokens
12.8K
Snippets
49
Records
51
Agent score
66%

What's inside Compose ImageLoader

  1. Understand the ImageLoader API and ImageAction lifecycle

    master

    The ImageLoader is the primary entry point for image loading. It provides an async method that takes an ImageRequest and returns a Flow<ImageAction>.

    ImageAction is a sealed interface representing the lifecycle of an image load request. It is divided into three main states:

    1. Loading: Indicates the request is in progress. This includes ImageEvent subtypes like Start, StartWithMemory, StartWithDisk, and StartWithFetch.
    2. Success: Indicates the image was loaded successfully. Common implementations include ImageResult.OfBitmap, ImageResult.OfImage, and ImageResult.OfPainter.
    3. Failure: Indicates an error occurred. It contains a Throwable via the error property. Common implementations include ImageResult.OfError and ImageResult.OfSource.
    interface ImageLoader {
        fun async(request: ImageRequest): Flow<ImageAction>
    }
    
    sealed interface ImageAction {
        sealed interface Loading : ImageAction
        sealed interface Success : ImageAction
        sealed interface Failure : ImageAction {
            val error: Throwable
        }
    }
  2. Configure ImageLoader cache interceptors

    master

    You can fine-tune memory and disk caching using the interceptor block within the ImageLoader configuration.

    Available configuration methods:

    • bitmapMemoryCacheConfig: Configures the bitmap cache (e.g., maxSize(bytes) or maxSizePercent(context, percent)).
    • imageMemoryCacheConfig: Configures the image cache (e.g., maxSize(count)).
    • painterMemoryCacheConfig: Configures the painter cache (e.g., maxSize(count)).
    • diskCacheConfig: Configures the disk cache, including directory and maxSizeBytes.
  3. Deploy the website

    master

    The project provides a yarn deploy command to facilitate deployment. Depending on your environment, use one of the following methods:

    Using SSH: Set the USE_SSH environment variable to true.

    Using GitHub Pages (Non-SSH): Provide your GitHub username via the GIT_USER environment variable. This command builds the website and pushes the content to the gh-pages branch.

    # Using SSH
    $ USE_SSH=true yarn deploy
    
    # Using GitHub Pages
    $ GIT_USER=<Your GitHub username> yarn deploy
  4. Configure Compose ImageLoader using Gradle Version Catalog

    master

    If your project uses a Gradle Version Catalog (libs.versions.toml), add the following entries to manage the Compose ImageLoader dependencies and their versions centrally.

    [versions]
    image-loader = "1.10.0"
    
    [libraries]
    image-loader = { module = "io.github.qdsfdhvh:image-loader", version.ref = "image-loader" }
    image-loader-extension-moko-resources = { module = "io.github.qdsfdhvh:image-loader-extension-moko-resources", version.ref = "image-loader" }
    image-loader-extension-blur = { module = "io.github.qdsfdhvh:image-loader-extension-blur", version.ref = "image-loader" }
    image-loader-extension-imageio = { module = "io.github.qdsfdhvh:image-loader-extension-imageio", version.ref = "image-loader" }
  5. Apply blur effects using BlurInterceptor

    master

    To enable blur effects in your image loading pipeline, you must register the BlurInterceptor within your ImageLoader configuration. Once registered, you can apply a blur effect to individual image requests by calling the .blur() method on an ImageRequest and specifying a blurRadius.

    val imageLoader = ImageLoader {
        // ...
        interceptor {
            addInterceptor(BlurInterceptor())
        }
    }
    
    val request = ImageRequest {
        data("https://...")
        blur(blurRadius = 15)
    }
  6. Quick start with Compose ImageLoader

    master

    Compose ImageLoader provides several ways to display images in Kotlin Multiplatform Compose applications. Depending on your version and desired level of control, you can use high-level components like AutoSizeImage and AutoSizeBox, or the lower-level rememberImagePainter API.

    A high-level, convenient component for displaying images with automatic sizing.

    A component that provides lifecycle actions (Success, Loading, Failure), allowing you to render custom UI (like loading spinners or error icons) based on the image state.

    Option 3: Using rememberImagePainter

    A lower-level approach for retrieving a Painter from a URL or resource, suitable for use with the standard Compose Image component.

    @Composeable
    fun Content() {
        // Option 1 on 1.7.0+
        AutoSizeImage(
            "https://...",
            contentDescription = "image",
        )
    
        // Option 2 on 1.7.0+
        AutoSizeBox("https://...") { action ->
            when (action) {
                is ImageAction.Success -> {
                    Image(
                        rememberImageSuccessPainter(action),
                        contentDescription = "image",
                    )
                }
                is ImageAction.Loading -> {}
                is ImageAction.Failure -> {}
            }
        }
    
        // Option 3
        Image(
            painter = rememberImagePainter("https://.."),
            contentDescription = "image",
        )
    }
  7. Integrate Moko Resources with Compose ImageLoader

    master

    To support Moko Resources (specifically AssetResource, ColorResource, FileResource, and ImageResource), you must add the MokoResourceFetcher.Factory to your ImageLoader components. If you are targeting Android, you must also provide the androidContext in the options block.

    Note: The androidContext requirement is specific to the Android target.

    val imageLoader = ImageLoader {
        components {
            add(MokoResourceFetcher.Factory())
        }
        
        // Required for Android target
        options {
            androidContext(applicationContext)
        }
    }
  8. Install Compose ImageLoader

    master

    Add the core dependency to your commonMain sourceSet in build.gradle.kts. You can also add optional extensions for specific resource decoders or effects.

    Core Dependency:

    • io.github.qdsfdhvh:image-loader:1.10.0

    Optional Extensions:

    • Compose Multiplatform Resources Decoder: io.github.qdsfdhvh:image-loader-extension-compose-resources:1.10.0
    • Moko Resources Decoder: io.github.qdsfdhvh:image-loader-extension-moko-resources:1.10.0
    • Blur Interceptor (Bitmap only): io.github.qdsfdhvh:image-loader-extension-blur:1.10.0
    • JVM ImageIO Decoder: io.github.qdsfdhvh:image-loader-extension-imageio:1.10.0 (add to jvmMain)
    kotlin {
        sourceSets {
            val commonMain by getting {
                dependencies {
                    api("io.github.qdsfdhvh:image-loader:1.10.0")
                    // optional extensions
                    api("io.github.qdsfdhvh:image-loader-extension-compose-resources:1.10.0")
                    api("io.github.qdsfdhvh:image-loader-extension-moko-resources:1.10.0")
                    api("io.github.qdsfdhvh:image-loader-extension-blur:1.10.0")
                }
            }
            val jvmMain by getting {
                dependencies {
                    api("io.github.qdsfdhvh:image-loader-extension-imageio:1.10.0")
                }
            }
        }
    }
  9. Configure a custom ImageLoader

    master

    To avoid unnecessary reloads and manage caching, create a custom ImageLoader and provide it to your UI tree using CompositionLocalProvider with LocalImageLoader.

    There are three main ways to consume images, listed in order of recommended priority:

    1. AutoSizeImage: High-level component (based on Modifier.Node).
    2. AutoSizeBox: Provides lifecycle actions (Success, Loading, Failure).
    3. rememberImagePainter: The standard painter-based approach.

    Configuration is typically done per-platform (Android, JVM, iOS) to handle platform-specific contexts or file paths.

    @Composable
    fun Content() {
        CompositionLocalProvider(
            LocalImageLoader provides remember { generateImageLoader() },
        ) {
            // Option 1: Recommended
            AutoSizeImage(
                "https://...",
                contentDescription = "image",
            )
    
            // Option 2: For lifecycle handling
            AutoSizeBox("https://...") { action ->
                when (action) {
                    is ImageAction.Success -> {
                        Image(
                            rememberImageSuccessPainter(action),
                            contentDescription = "image",
                        )
                    }
                    is ImageAction.Loading -> { /* Show loader */ }
                    is ImageAction.Failure -> { /* Show error */ }
                }
            }
    
            // Option 3: Standard
            Image(
                painter = rememberImagePainter("https://.."),
                contentDescription = "image",
            )
        }
    }
  10. Display images in Compose

    master

    Compose ImageLoader provides several ways to display images depending on your Compose version and required level of control.

    Recommended Priority:

    1. AutoSizeImage: The simplest high-level component (available on 1.7.0+).
    2. AutoSizeBox: Provides lifecycle hooks for Success, Loading, and Failure states (available on 1.7.0+).
    3. rememberImagePainter: The standard painter-based approach.

    Note: AutoSizeBox and AutoSizeImage are built using Modifier.Node for better performance. AutoSizeImage is essentially a convenience wrapper around AutoSizeBox + Painter.

    // Option 1: Simplest (1.7.0+)
    AutoSizeImage(
        "https://...",
        contentDescription = "image",
    )
    
    // Option 2: With state handling (1.7.0+)
    AutoSizeBox("https://...") { action ->
        when (action) {
            is ImageAction.Success -> {
                Image(
                    rememberImageSuccessPainter(action),
                    contentDescription = "image",
                )
            }
            is ImageAction.Loading -> { /* Show loader */ }
            is ImageAction.Failure -> { /* Show error */ }
        }
    }
    
    // Option 3: Standard Painter
    Image(
        painter = rememberImagePainter("https://.."),
        contentDescription = "image",
    )