ImagePickerKMP
ImagePickerKMP – Cross‑platform Image Picker & Camera Library (Android & iOS) built with Kotlin Multiplatform + Compose Multiplatform.
Install / Use
/learn @ismoy/ImagePickerKMPREADME
ImagePickerKMP
Cross-platform Image Picker & Camera Library for Kotlin Multiplatform
Easily capture or select images on Android, iOS, Desktop, and Web — all with a single API.
Built with Compose Multiplatform, designed for simplicity, performance, and flexibility.
<p align="center"> <a href="https://imagepickerkmp.dev/"> <img src="https://img.shields.io/badge/%20Full%20Documentation-Visit%20Docs%20Site-0ea5e9?style=for-the-badge&logoColor=white" alt="Documentation Site"> </a> <a href="https://github.com/sponsors/ismoy"> <img src="https://img.shields.io/badge/Sponsor-%E2%9D%A4-red?style=for-the-badge&logo=github" alt="Sponsor"> </a> </p>
ImagePickerKMP saves you 2 weeks of native Android/iOS/Web integration work.
It's free and open source. If your app or company benefits from it, consider sponsoring to keep it maintained and updated with every new KMP/Compose release.
→ Become a sponsor
Example
Complete Example App
Full-featured sample application showcasing:
- All library features and configurations
Quick Start
⚠️ Requirements
| Requirement | Minimum version |
|---|---|
| Kotlin | 2.3.20 (breaking change — see CHANGELOG) |
| Compose Multiplatform | 1.10.3 |
| Ktor | 3.4.1 |
| Android minSdk | 24 |
| Android compileSdk | 36 |
Note: This library is compiled with Kotlin 2.3.20. Projects using Kotlin < 2.3.x will get an ABI incompatibility error at compile time. If you need Kotlin 2.1.x support, use a previous version of this library.
Installation
Kotlin Multiplatform:
dependencies {
implementation("io.github.ismoy:imagepickerkmp:1.0.35-alpha1")
}
React/JavaScript:
npm install imagepickerkmp
New — rememberImagePickerKMP (recommended)
The modern, idiomatic Compose API. A single state holder — no manual booleans, no Render() call needed.
@Composable
fun basicUsageScreen() {
val picker = rememberImagePickerKMP(
config = ImagePickerKMPConfig(
galleryConfig = GalleryConfig(
allowMultiple = true,
selectionLimit = 20
)
)
)
val result = picker.result
Scaffold(
modifier = Modifier
.fillMaxSize(),
topBar = {
TopAppBar(
title = { Text("Basic Usage") },
navigationIcon = {
IconButton(onClick = {}) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Go back"
)
}
}
)
},
bottomBar = {
BottomAppBar {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(
onClick = { picker.launchCamera() },
modifier = Modifier.weight(1f)
) {
Text("Camera")
}
Button(
onClick = { picker.launchGallery() },
modifier = Modifier.weight(1f)
) {
Text("Gallery")
}
}
}
}
){scaffoldPadding->
Column(
modifier = Modifier
.padding(scaffoldPadding)
.fillMaxSize()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
contentAlignment = Alignment.Center
) {
when (result) {
is ImagePickerResult.Loading -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(16.dp)
) {
CircularProgressIndicator()
Text(
text = "Loading...",
color = Color.Gray,
modifier = Modifier.padding(top = 12.dp)
)
}
}
is ImagePickerResult.Success -> {
// Result here
}
is ImagePickerResult.Error -> {
Text(
text = "Error: ${result.exception.message}",
color = Color.Red,
modifier = Modifier.padding(16.dp)
)
}
is ImagePickerResult.Dismissed -> {
Text("Selection cancelled", color = Color.Gray)
}
is ImagePickerResult.Idle -> {
Text("Press a button to get started", color = Color.Gray)
}
}
}
}
}
}
Per-launch overrides:
// Override gallery options for a single launch
picker.launchGallery(
allowMultiple = true,
selectionLimit = 5,
mimeTypes = listOf(MimeType.IMAGE_JPEG),
includeExif = true
)
// Override camera options for a single launch
picker.launchCamera(
cameraCaptureConfig = CameraCaptureConfig(compressionLevel = CompressionLevel.HIGH),
enableCrop = false
)
New API vs Legacy API — Migration Guide
TL;DR: Use
rememberImagePickerKMPfor all new code. The legacyImagePickerLauncher/GalleryPickerLauncherare deprecated and will be removed in a future major release.
Side-by-side comparison
| | Legacy API (v1) — Deprecated | New API (v2) — Recommended |
|---|---|---|
| Camera | ImagePickerLauncher(config = ...) | picker.launchCamera() |
| Gallery | GalleryPickerLauncher(...) | picker.launchGallery() |
| Result handling | Callbacks (onPhotoCaptured, onDismiss, onError) | Reactive when (picker.result) |
| State management | Manual showCamera, showGallery booleans | Automatic via ImagePickerKMPState |
| Per-launch config | Not supported | Override any param on each launch*() call |
| Reset | Call onDismiss callback | picker.reset() |
| Configuration | ImagePickerConfig + GalleryPickerConfig | ImagePickerKMPConfig (unified) |
Migration table
| Legacy pattern | New API equivalent |
|---|---|
| showCamera = true | picker.launchCamera() |
| showGallery = true | picker.launchGallery() |
| onPhotoCaptured = { result -> ... } | is ImagePickerResult.Success -> result.photos |
| `onDismis
Related Skills
node-connect
353.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
353.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
353.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
