Skip to content

Compose Image library for Kotlin Multiplatform.

License

Notifications You must be signed in to change notification settings

qdsfdhvh/compose-imageloader

Folders and files

NameName
Last commit message
Last commit date
Jan 22, 2025
Feb 19, 2024
Jan 15, 2025
Jan 16, 2025
Jan 17, 2025
Jan 16, 2025
Feb 12, 2025
Jul 3, 2024
Jan 16, 2025
Jan 15, 2025
Jan 15, 2025
Jan 15, 2025
Oct 24, 2023
Oct 24, 2023
Jan 15, 2025
Jun 17, 2022
Jan 17, 2025
Jan 17, 2025
Apr 21, 2024
Dec 20, 2024
Jul 11, 2024
Jan 26, 2024
Feb 16, 2023
Jan 23, 2025

Repository files navigation

Compose ImageLoader

Maven Central

Compose Image library for Kotlin Multiplatform.

Setup

Add the dependency in your common module's commonMain sourceSet

kotlin {
    android()
    ios()
    // ...

    sourceSets {
        val commonMain by getting {
            dependencies {
+                api("io.github.qdsfdhvh:image-loader:1.10.0")
                // optional - Compose Multiplatform Resources Decoder
+                api("io.github.qdsfdhvh:image-loader-extension-compose-resources:1.10.0")
                // optional - Moko Resources Decoder
+                api("io.github.qdsfdhvh:image-loader-extension-moko-resources:1.10.0")
                // optional - Blur Interceptor (only support bitmap)
+                api("io.github.qdsfdhvh:image-loader-extension-blur:1.10.0")
            }
        }
        val jvmMain by getting {
            dependencies {
                // optional - ImageIO Decoder
+                api("io.github.qdsfdhvh:image-loader-extension-imageio:1.10.0")
            }
        }
    }
}

How to Use

Display Image

val painter = rememberImagePainter("https://..")
Image(
    painter = painter,
    contentDescription = "image",
)

PS: default Imageloader will reload when it's displayed, is not friendly for https link, so it is recommended to custom ImageLoader and configure the cache.

Custom ImageLoader

I configure the Imageloader {} on each platform, you also can configure it in the commonMain like Tachidesk-JUI.

@Composable
fun Content() {
    CompositionLocalProvider(
        LocalImageLoader provides remember { generateImageLoader() },
    ) {
        // 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",
        )
    }
}

Use priority: AutoSizeImage -> AutoSizeBox -> rememberImagePainter.

AutoSizeBox & AutoSizeImage are based on Modifier.Node, AutoSizeImageAutoSizeBox + Painter.

in Android

fun generateImageLoader(): ImageLoader {
    return ImageLoader {
        options {
            androidContext(applicationContext)
        }
        components {
            setupDefaultComponents()
        }
        interceptor {
            // cache 25% memory bitmap
            bitmapMemoryCacheConfig {
                maxSizePercent(context, 0.25)
            }
            // cache 50 image
            imageMemoryCacheConfig {
                maxSize(50)
            }
            // cache 50 painter
            painterMemoryCacheConfig {
                maxSize(50)
            }
            diskCacheConfig {
                directory(context.cacheDir.resolve("image_cache").toOkioPath())
                maxSizeBytes(512L * 1024 * 1024) // 512MB
            }
        }
    }
}

in Jvm

fun generateImageLoader(): ImageLoader {
    return ImageLoader {
        components {
            setupDefaultComponents()
        }
        interceptor {
            // cache 32MB bitmap
            bitmapMemoryCacheConfig {
                maxSize(32 * 1024 * 1024) // 32MB
            }
            // cache 50 image
            imageMemoryCacheConfig {
                maxSize(50)
            }
            // cache 50 painter
            painterMemoryCacheConfig {
                maxSize(50)
            }
            diskCacheConfig {
                directory(getCacheDir().toOkioPath().resolve("image_cache"))
                maxSizeBytes(512L * 1024 * 1024) // 512MB
            }
        }
    }
}

// about currentOperatingSystem, see app
private fun getCacheDir() = when (currentOperatingSystem) {
    OperatingSystem.Windows -> File(System.getenv("AppData"), "$ApplicationName/cache")
    OperatingSystem.Linux -> File(System.getProperty("user.home"), ".cache/$ApplicationName")
    OperatingSystem.MacOS -> File(System.getProperty("user.home"), "Library/Caches/$ApplicationName")
    else -> throw IllegalStateException("Unsupported operating system")
}

in iOS

fun generateImageLoader(): ImageLoader {
    return ImageLoader {
        components {
            setupDefaultComponents()
        }
       interceptor {
           // cache 32MB bitmap
           bitmapMemoryCacheConfig {
               maxSize(32 * 1024 * 1024) // 32MB
           }
           // cache 50 image
           imageMemoryCacheConfig {
               maxSize(50)
           }
           // cache 50 painter
           painterMemoryCacheConfig {
               maxSize(50)
           }
           diskCacheConfig {
               directory(getCacheDir().toPath().resolve("image_cache"))
               maxSizeBytes(512L * 1024 * 1024) // 512MB
           }
        }
    }
}

private fun getCacheDir(): String {
    return NSSearchPathForDirectoriesInDomains(
        NSCachesDirectory,
        NSUserDomainMask,
        true,
    ).first() as String
}

for more platform targets, see app.

ImageRequest

val imageRequest = ImageRequest {
    data(url)
    addInterceptor(DoSomthingInterceptor())
    components {
        // ...
    }
    extra {
        set("key_int", 11)
    }
}
val newImageRequest = ImageRequest(imageRequest) {
    // ...
}

Before 1.2.8

LocalImageLoader has no default value, you must be configured on each platform, and configuration is similar to coil.

@Composable
fun Content() {
    CompositionLocalProvider(
        LocalImageLoader provides remember { generateImageLoader() },
    ) {
        val painter = rememberAsyncImagePainter("https://.....")
        Image(
            painter = painter,
            contentDescription = "image",
        )
    }
}

fun generateImageLoader(): ImageLoader {
    return ImageLoaderBuilder().build()
}

Thx

Coil