DEV Community

Cover image for 📸 JetImagePicker: Effortless Image Picking in Jetpack Compose (With Zero Permission Worries)
Nyerhovwo Adjekughene (Nero)
Nyerhovwo Adjekughene (Nero)

Posted on

📸 JetImagePicker: Effortless Image Picking in Jetpack Compose (With Zero Permission Worries)

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:

  1. Handles camera/gallery permissions automatically
  2. Supports multiple image selection
  3. Handles Android 13+ scoped media permissions
  4. Provides compression, resizing, and easy-to-use UI previews
  5. 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
            )
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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' }
    }
}
Enter fullscreen mode Exit fullscreen mode

// app/build.gradle

dependencies {
    implementation 'com.github.nerojust:JetImagePicker:v1'
}
Enter fullscreen mode Exit fullscreen mode

//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" />
Enter fullscreen mode Exit fullscreen mode

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

AndroidDev #Kotlin #JetpackCompose #Permissions #MobileDevelopment #CleanArchitecture #OpenSource #DevTools

Top comments (0)