Skip to content

Repository files navigation

Wannaverse Logo

Build iOS Build Linux

Image Selector

A Kotlin Multiplatform image selection library for Android, iOS and Desktop.

Features

  • Selecting images on Android, iOS and Desktop
  • Setting aspect ratios
  • Compressing images by quality or toward a target file size

Supported Platform

Platform Supported
Android ✔️
iOS ✔️
Desktop ✔️
Web ❌️

Installation

See the releases section of this repository for the latest version.

To your build.gradle under commonMain.dependencies add:

implementation "com.wannaverse:imageselector:<version>"

Important this library uses native code. You will need the additional dependencies depending on the targets you are building for:

Android under androidMain.dependencies: implementation("com.wannaverse:imageselector-android:")

iOS (ARM) under iosMain.dependencies: implementation("com.wannaverse:imageselector-iosarm64:")

iOS x64 under iosMain.dependencies: implementation("com.wannaverse:imageselector-iosx64:")

jvm: implementation("com.wannaverse:imageselector-jvm:")

Usage

KDocs

Below is a sample code that you may use.

When setting up the image selector on Android, you will need to add the following to your MainActivity's onCreate function:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    // Add these to register the image selector
    setImageSelectorActivity(this)
    registerImageSelectorLauncher()
}

Example ViewModel:

class AppViewModel : ViewModel() {
    val image = mutableStateOf<ImageData?>(null)
    val showLoadingState by mutableStateOf(false)
    fun chooseImage() = viewModelScope.launch {
        image.value = selectImage( loadingState = { showLoadingState = it } )
    }
}

Note: selectImage() return a downscaled image for the current window to prevent OutOfMemoryError. That's why it may take several seconds to return the image bytes. So, a loadingState callback is provided to show a CircularProgressIndicator to make the UI look responsive instead of frozen during image processing.

Example Composable:

@Composable
fun App(viewModel: AppViewModel = viewModel { AppViewModel() }) {
    val image = remember { viewModel.image }
    val bitmap = image.value?.bytes?.toImageBitmap()

    if(viewModel.showLoadingState) {
        Dialog(
            onDismissRequest = { /* Non-Dismissible */ } 
        ) {
            CircularProgressIndicator()
        }
    }

    Column(modifier = Modifier
        .fillMaxSize()
        .safeContentPadding()
    ) {
        Button(
            modifier = Modifier.align(Alignment.CenterHorizontally),
            onClick = { viewModel.chooseImage() }
        ) {
            Text(
                text = "Select Image"
            )
        }
        bitmap?.let {
            Image(
                bitmap = it,
                contentDescription = null
            )
        }
    }
}

You can then compress the selected image when needed:

val compressed = image.value?.compress(
    ImageCompressionOptions(
        quality = 88
    )
)

Or target a maximum size, for example 5 MB:

val underFiveMb = image.value?.compressToMaxBytes(
    maxBytes = 5L * 1024L * 1024L
)

Note: maxBytes is only enforced for JPEG images. PNG is a lossless format with no quality parameter, so the maxBytes constraint is ignored when compressing to PNG, the image will be returned at full size.

License

MIT LICENSE. See LICENSE for details.

Contributing

Pull requests and feature requests are welcome! If you encounter any issues, feel free to open an issue.