Cross‑platform Image Picker & Camera Library (Android & iOS)
Built with Kotlin Multiplatform + Compose Multiplatform + Kotlin/Native
Español • GitHub • Maven Central • Discord
- Cross-platform: Works seamlessly on Android and iOS
- Camera Integration: Direct camera access with photo capture
- Customizable UI: Custom dialogs and confirmation views
- Permission Handling: Smart permission management for both platforms
- Easy Integration: Simple API with Compose Multiplatform
- Highly Configurable: Extensive customization options
In your commonMain build.gradle.kts:
dependencies {
implementation("io.github.ismoy:imagepickerkmp:1.0.21")//lastversion
}Don't forget to configure iOS-specific permissions in your Info.plist file:
<key>NSCameraUsageDescription</key>
<string>We need access to the camera to capture a photo.</string> var showCamera by remember { mutableStateOf(false) }
var capturedPhoto by remember { mutableStateOf<CameraPhotoHandler.PhotoResult?>(null) }if (showCamera) {
ImagePickerLauncher(
config = ImagePickerConfig(
onPhotoCaptured = { result ->
capturedPhoto = result
showCamera = false
},
onError = {
showCamera = false
},
onDismiss = {
showImagePicker = false // Reset state when user doesn't select anything
},
// Custom dialog only IOS optional delete it for use default UI
customPickerDialog = { onTakePhoto, onSelectFromGallery, onCancel ->
MyCustomBottomSheet(
onTakePhoto = onTakePhoto,
onSelectFromGallery = onSelectFromGallery,
onDismiss = {
isPickerSheetVisible = false
onCancel()
showCameraPicker = false
}
)
},
// Only Android optional delete it for use default UI
cameraCaptureConfig = CameraCaptureConfig(
permissionAndConfirmationConfig = PermissionAndConfirmationConfig(
customConfirmationView = { photoResult, onConfirm, onRetry ->
YourCustomConfirmationView(
result = photoResult,
onConfirm = onConfirm,
onRetry = onRetry
)
},
// Optional: custom permission handler delete it for use default UI
customPermissionHandler = { config ->
// Handle permissions here
}
)
)
)
)
}var showGallery by remember { mutableStateOf(false) }
var selectedImages by remember { mutableStateOf<List<GalleryPhotoHandler.PhotoResult>>(emptyList()) }if (showGallery) {
GalleryPickerLauncher(
onPhotosSelected = { photos ->
selectedImages = photos
showGallery = false
},
onError = { error ->
showGallery = false
},
onDismiss = {
println("User cancelled or dismissed the picker")
showGallery = false // Reset state when user doesn't select anything
}
allowMultiple = true, // False for single selection
mimeTypes = listOf("image/jpeg", "image/png") ,// Optional: filter by type
// Only Android optional delete it for use default UI
cameraCaptureConfig = CameraCaptureConfig(
permissionAndConfirmationConfig = PermissionAndConfirmationConfig(
customConfirmationView = { photoResult, onConfirm, onRetry ->
YourCustomConfirmationView(
result = photoResult,
onConfirm = onConfirm,
onRetry = onRetry
)
}
)
)
)
}
Button(onClick = { showGallery = true }) {
Text("Choose from Gallery")
}For more customization (confirmation views, MIME filtering, etc.), check out the integration guide for KMP.
Even if you're not using KMP, you can use ImagePickerKMP in pure Android projects with Jetpack Compose.
implementation("io.github.ismoy:imagepickerkmp:1.0.21")var showCamera by remember { mutableStateOf(false) }
var capturedPhoto by remember { mutableStateOf<CameraPhotoHandler.PhotoResult?>(null) }if (showCamera) {
ImagePickerLauncher(
config = ImagePickerConfig(
onPhotoCaptured = { result ->
capturedPhoto = result
showCamera = false
},
onError = {
showCamera = false
},
onDismiss = {
showImagePicker = false // Reset state when user doesn't select anything
},
// Custom dialog only IOS optional delete it for use default UI
customPickerDialog = { onTakePhoto, onSelectFromGallery, onCancel ->
MyCustomBottomSheet(
onTakePhoto = onTakePhoto,
onSelectFromGallery = onSelectFromGallery,
onDismiss = {
isPickerSheetVisible = false
onCancel()
showCameraPicker = false
}
)
},
// Only Android optional delete it for use default UI
cameraCaptureConfig = CameraCaptureConfig(
permissionAndConfirmationConfig = PermissionAndConfirmationConfig(
customConfirmationView = { photoResult, onConfirm, onRetry ->
YourCustomConfirmationView(
result = photoResult,
onConfirm = onConfirm,
onRetry = onRetry
)
},
// Optional: custom permission handler delete it for use default UI
customPermissionHandler = { config ->
// Handle permissions here
}
)
)
)
)
}
Button(onClick = { showCamera = true }) {
Text("Take Photo")
}var showGallery by remember { mutableStateOf(false) }
var selectedImages by remember { mutableStateOf<List<GalleryPhotoHandler.PhotoResult>>(emptyList()) }if (showGallery) {
GalleryPickerLauncher(
onPhotosSelected = { photos ->
selectedImages = photos
showGallery = false
},
onError = { error ->
showGallery = false
},
onDismiss = {
showCamera = false
},
allowMultiple = true, // False for single selection
mimeTypes = listOf("image/jpeg", "image/png"), // Optional: filter by type
// Only Android optional
cameraCaptureConfig = CameraCaptureConfig(
permissionAndConfirmationConfig = PermissionAndConfirmationConfig(
customConfirmationView = { photoResult, onConfirm, onRetry ->
YourCustomConfirmationView(
result = photoResult,
onConfirm = onConfirm,
onRetry = onRetry
)
}
)
)
)
}
Button(onClick = { showGallery = true }) {
Text("Choose from Gallery")
}See the Android Native integration guide for more usage details.
- On Android, the user will see the system gallery picker.
- On iOS, the native gallery picker is used. On iOS 14+, multiple selection is supported. The system handles permissions and limited access natively.
- The callback
onPhotosSelectedalways receives a list, even for single selection. - You can use
allowMultipleto enable or disable multi-image selection. - The
mimeTypesparameter is optional and lets you filter selectable file types.
The GalleryPickerLauncher now includes an onDismiss callback to handle cases where users dismiss the picker without selecting any images. This resolves the issue where the picker couldn't be reopened after being dismissed.
The onDismiss callback is triggered when:
- User cancels the selection dialog (Android)
- User taps "Cancel" (iOS)
- User cancels camera permission request (iOS)
- User cancels the camera interface (taps "Cancel" or "X") (iOS)
Cross-platform compatibility with intelligent context management
- Android: The library automatically manages the context using
LocalContext.current. No need to pass context manually. - iOS: Context is not required as the library uses native iOS APIs.
| Platform | Minimum Version | Status |
|---|---|---|
| Android | API 21+ | ✅ |
| iOS | iOS 12.0+ | ✅ |
| Compose Multiplatform | 1.5.0+ | ✅ |
The most comprehensive and developer-friendly image picker for Kotlin Multiplatform
| Feature | ImagePickerKMP | Peekaboo | KMPImagePicker |
|---|---|---|---|
| Compose Multiplatform Support | ✅ Native | ❌ Android only | |
| UI Customization | ✅ Full control | ||
| Unified Permissions | ✅ Smart handling | ❌ Manual | |
| Error Handling | ✅ Comprehensive | ||
| Camera Integration | ✅ Direct access | ✅ Direct access | |
| Gallery Support | ✅ Multi-select | ✅ Multi-select | ✅ Multi-select |
| Cross-platform API | ✅ Single codebase | ❌ Platform-specific |
- ** Compose Multiplatform Native**: Built specifically for Compose Multiplatform, ensuring consistent behavior across platforms
- ** Full UI Customization**: Complete control over dialogs, confirmation views, and camera UI
- ** Smart Permission Management**: Unified permission handling with intelligent fallbacks
- ** Performance Optimized**: Efficient image processing and memory management
- ** Developer Friendly**: Simple API with comprehensive error handling
- Minimum SDK: 21
- Kotlin 1.8+
- Compose Multiplatform
- iOS 12.0+
- Xcode 14+
- Kotlin Multiplatform
For detailed Android integration guide, see: Android Integration Guide
For detailed iOS integration guide, see: iOS Integration Guide
For detailed KMP integration guide, see: Kotlin Multiplatform Integration Guide
Comprehensive guides and references for every aspect of ImagePickerKMP
- Integration Guide - Complete setup and integration guide
- Customization Guide - UI and behavior customization
- Internationalization Guide - Multi-language support guide
- Permissions Guide - Permission handling details
- Coverage Guide - Code coverage and testing guide
- Notifications Setup - Discord notifications setup
- API Reference - Complete API documentation
- Examples - Code examples and use cases
We welcome contributions from the community!
See our Contributing Guide for details.
This project is licensed under the MIT License
See the LICENSE file for details.
Get help, report issues, or join our community
📧 Email • 🐛 Issues • 📖 Wiki • 💬 Discord
See CHANGELOG.md for a complete list of changes and updates.
Made with ❤️ for the Kotlin Multiplatform community
Star ⭐ this repo if it helped you!
⭐ Star on GitHub • 🍴 Fork • 🐛 Report Bug • 💡 Request Feature