Kotlin Multiplatform, Compose Multiplatform and Flutter

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

See the section bibliography for the reference material consulted while preparing these pages.

Baseline date: 2026-10-07. This page is shorter than the platform pages, because the multiplatform tools reuse their APIs. The Kotlin Multiplatform section is at Kotlin Multiplatform, and the native details are in Android and iOS.

Coil 3 in Compose Multiplatform

Coil 3 is a Kotlin Multiplatform library: the same AsyncImage runs on Android, iOS, desktop and web in commonMain. A network fetcher artifact is required for each target (coil-network-ktor3 is the multiplatform choice, and coil-network-okhttp is Android and JVM only).

// commonMain
dependencies {
    implementation("io.coil-kt.coil3:coil-compose:3.6.3")
    implementation("io.coil-kt.coil3:coil-network-ktor3:3.6.3")
}
// plus one Ktor engine per platform: ktor-client-okhttp (Android), ktor-client-darwin (iOS), ktor-client-cio (desktop)
@Composable
fun Cover(url: String, blurhash: String?) {
    AsyncImage(
        model = url,
        contentDescription = null,
        placeholder = rememberBlurHashPainter(blurhash),   // from the KMP BlurHash library, below
        contentScale = ContentScale.Crop,
        modifier = Modifier.aspectRatio(2f / 3f),
    )
}

Cache sizes and the loader are configured with SingletonImageLoader.setSafe in common code or the platform entry points, see Coil getting started. Format support follows the platform: Skia decodes JPEG, PNG and WebP, and AVIF depends on the Skia build and the platform version, so serve WebP or JPEG when you are unsure.

BlurHash in Kotlin Multiplatform

vanniktech/blurhash (Maven Central, 0.3.0) decodes and encodes BlurHash in common code, and converts the result to a Compose ImageBitmap. Check its README for the exact functions in the version you install, then wrap the call in a remember:

@Composable
fun rememberBlurHashPainter(hash: String?): Painter? = remember(hash) {
    hash?.let { decodeBlurHashToImageBitmap(it, 32, 32) }?.let { BitmapPainter(it) }   // your wrapper over the library call
}

The algorithm is in the BlurHash page. A fully shared decoder avoids keeping separate Kotlin and Swift copies of the woltapp files.

Video with expect and actual

There is no shared multiplatform video player in the Compose Multiplatform core. The usual pattern is an expect composable with an actual per platform, each wrapping the native player:

// commonMain
@Composable
expect fun VideoPlayer(url: String, modifier: Modifier = Modifier)

// androidMain: Media3 ExoPlayer inside AndroidView, see the Media3 page
@Composable
actual fun VideoPlayer(url: String, modifier: Modifier) { /* AndroidView { PlayerView(it).apply { player = exoPlayer } } */ }

// iosMain: AVPlayerViewController inside UIKitViewController, see the AVFoundation page
@Composable
actual fun VideoPlayer(url: String, modifier: Modifier) { /* UIKitViewController(factory = { AVPlayerViewController() }) */ }

Both platforms play HLS natively, so one backend manifest serves both, see Media3 and AVFoundation. Libraries that provide a ready-made multiplatform player exist (search the KMP library index), so check their maintenance status first.

Flutter

Flutter has the same needs and equivalent packages:

  • cached_network_image shows a network image with a placeholder and a fade-in, backed by a disk cache and a memory cache. Use memCacheWidth to decode at the displayed size.

  • flutter_blurhash: BlurHash(hash: hash, image: url) shows the hash while the image loads, and can also be used as the placeholder of CachedNetworkImage.

  • video_player plays HLS through ExoPlayer and AVPlayer.

CachedNetworkImage(
  imageUrl: url,
  memCacheWidth: 640,
  placeholder: (context, url) => BlurHash(hash: blurhash),
  fit: BoxFit.cover,
)

Choose the variant by the logical size times the device pixel ratio, as in the other platform pages.