Let's face it - handling image capture and gallery selection in Android is painful. Between READ_MEDIA_IMAGES, scoped storage, runtime permissions, compression, and Compose UI updates… it's easy to mess up. That's why I built JetImagePicker - a plug-and-play image picker for Android from API 21 to 34+ that just works.
What Is JetImagePicker?
JetImagePicker is a modern Kotlin library that:
- Handles camera/gallery permissions automatically
- Supports multiple image selection
- Handles Android 13+ scoped media permissions
- Provides compression, resizing, and easy-to-use UI previews
- Works with both Jetpack Compose and classic XML
Why I Built It
While working on photo uploads at Raenest, we found ourselves:
- Copy-pasting permission logic into every feature
- Handling edge cases manually for Android 10–14
- Writing boilerplate code for image compression, state handling, and previews
I thought: There has to be a cleaner way - so I created JetImagePickerLibrary.
Full Example in Jetpack Compose
@Composable
fun ImagePickerScreen(modifier: Modifier = Modifier) {
val context = LocalContext.current
var message by remember { mutableStateOf<String?>(null) }
val pickerState = rememberJetImagePickerState(
context = context,
config = JetImagePickerConfig(
enableCompression = true,
compressionQuality = 70,
allowMultiple = false,
targetWidth = 1024,
targetHeight = 1024
)
) { result ->
when (result) {
is ImagePickerResult.Success -> {
message = null
Log.d("ImagePicker", "Images selected: ${result.uris}")
}
is ImagePickerResult.PermissionDenied -> {
message = "Permission denied: ${result.permission}"
}
is ImagePickerResult.PermissionPermanentlyDenied -> {
message = "Permission permanently denied: ${result.permission}. Enable in settings."
}
is ImagePickerResult.ShowRationale -> {
message = "Please grant ${result.permission} permission to continue."
}
}
}
Column(modifier = modifier.padding(16.dp)) {
Button(
onClick = pickerState.pickFromGallery,
modifier = Modifier.fillMaxWidth()
) {
Text("Pick from Gallery")
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = pickerState.captureWithCamera,
modifier = Modifier.fillMaxWidth()
) {
Text("Capture with Camera")
}
Spacer(modifier = Modifier.height(16.dp))
when (pickerState.selectedImageUris.size) {
1 -> ImagePreview(uri = pickerState.selectedImageUris.first())
in 2..Int.MAX_VALUE -> MultiImagePreview(imageUris = pickerState.selectedImageUris)
}
message?.let {
Spacer(modifier = Modifier.height(12.dp))
Text(
text = it,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall
)
}
}
}
Features Recap
Feature JetImagePicker Library
📱 API Level Support 21 to 34+
🔐 Runtime Permissions - Auto-handled
🖼 Multiple Image Support - Yes
🧯 Compression & Resizing - Configurable
🧩 Jetpack Compose Ready - Native
🎯 Zero Boilerplate - Yes
⚙️ How It Works
rememberJetImagePickerState manages all state, permissions, and result callbacks
JetImagePickerConfig lets you control compression, multi-selection, and image dimensions
Preview components (ImagePreview, MultiImagePreview) are provided out of the box
🔌 Installation
// settings.gradle
dependencyResolutionManagement {
repositories {
maven { url 'https://jitpack.io' }
}
}
// app/build.gradle
dependencies {
implementation 'com.github.nerojust:JetImagePicker:v1'
}
//In android manifest
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
Check out the library on Github Here
Final Thoughts
If you're building modern Android apps with Jetpack Compose or XML, JetImagePickerLibrary will save you dozens of hours. No more worrying about runtime permissions or fragile file URI logic.
One function. All versions. All scenarios handled.
Want to Contribute?
Got improvements, Compose UI integrations, or better compression ideas?
📦 Fork it on GitHub: here
🤝 Let's Connect
💼 LinkedIn
🐙 GitHub
I share posts weekly on:
📱 Android Dev • 🧠 Clean Architecture • ⚙️ Jetpack • 🔐 Security • 🌍 Fintech Engineering
🏷 Tags
Top comments (0)