icorp is a modern, premium, and fully platform-agnostic image cropping library built using Kotlin Multiplatform and Jetpack Compose Multiplatform. It targets Android, iOS, and Desktop (JVM) with 100% shared UI and business logic.
- Stretchable & Resizable Selection: Drag corners or borders to freely resize the crop frame. Drag the center to reposition the box.
- Image Panning & Zooming: Drag outside the crop box to pan the image. Use the horizontal tick-dial slider at the bottom to zoom the image (100% to 300%).
- Locked Boundaries: Constraints guarantee that the crop frame never goes out of the image bounds (prevents black borders).
- 90° Rotation: Rotate the target image clockwise with automatic bounds recalculation.
- Aspect Ratio Locking: Choose between Free, 1:1 (Square), 4:3, and 16:9 aspect ratios.
- Automatic Size Limit (< 1 MB): Any cropped image is automatically scaled down (maximum dimension of 1500px) if needed, guaranteeing a file size under 1 MB while preserving details.
This is the standard and most reliable way to consume the library. Since the library is published on Maven Central, you do not need to add any custom repositories.
- Add the Dependency:
In your target project's shared/common module
build.gradle.kts(e.g.,composeApp/build.gradle.kts), add the dependency:kotlin { sourceSets { commonMain.dependencies { implementation("io.github.kvarun701:icorp:1.0.0") } } }
You can also import the library directly from GitHub via JitPack.
-
Add the JitPack Repository: In your target project's root
settings.gradle.kts(orbuild.gradle.kts), add the JitPack maven repository:dependencyResolutionManagement { repositories { google() mavenCentral() maven("https://jitpack.io") // Add this line } } -
Add the Dependency: In your target project's shared/common module
build.gradle.kts(e.g.,composeApp/build.gradle.kts), add the dependency. ReplaceTagwith a release version (e.g.,1.0.0) or a specific commit hash:kotlin { sourceSets { commonMain.dependencies { implementation("com.github.kvarun701.icorp-library-for-composemultiplateform:icorp:Tag") } } }
This option compiles and installs the library into your local Maven cache, allowing any Compose Multiplatform project on your computer to import it.
- Publish the Library:
In your terminal, navigate to the project directory and run:
./gradlew :icorp:publishToMavenLocal
- Enable Local Maven in Target Project:
In your target project's root
settings.gradle.kts(orbuild.gradle.kts), make suremavenLocal()is in the repository list:dependencyResolutionManagement { repositories { google() mavenCentral() mavenLocal() // Add this line } } - Add the Dependency:
In your target project's shared/common module
build.gradle.kts(e.g.,composeApp/build.gradle.kts), declare the dependency:kotlin { sourceSets { commonMain.dependencies { implementation("io.github.kvarun701:icorp:1.0.0") } } }
-
Copy the
:libraryModule: Copy thelibraryfolder from theicorpdirectory into your target project directory. -
Include
:icorpin settings.gradle.kts: Open your target project'ssettings.gradle.ktsand include the module:include(":icorp") -
Add Project Dependency: In your target project's shared module
build.gradle.kts, add the project reference:dependencies { implementation(project(":icorp")) }
In your shared common code (commonMain), import the library and place the ImageCropper composable:
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.ImageBitmap
import icorp.ImageCropper
@Composable
fun EditPhotoScreen() {
var activeImage by remember { mutableStateOf<ImageBitmap>(/* Load your source ImageBitmap */) }
ImageCropper(
image = activeImage,
onCropSuccess = { croppedBitmap ->
// 1. The croppedBitmap is returned here
// 2. It is guaranteed to be under 1 MB in size
// 3. You can set it as the new active image or upload it
activeImage = croppedBitmap
},
onCancel = {
// Handle cancel action (e.g., close editor or revert changes)
}
)
}In your Android module's launcher activity, simply set the content to your shared composable:
package myproject.android
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import myproject.EditPhotoScreen // Your shared composable
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
EditPhotoScreen()
}
}
}- Export target: Create an entry point inside your library's
iosMaindirectory:package icorp import androidx.compose.ui.window.ComposeUIViewController fun MainViewController() = ComposeUIViewController { App() // Your shared Composable }
- Xcode Build Phase: In your Xcode project settings, go to Build Phases, add a Run Script build phase at the top (before compile sources), and run:
cd "$SRCROOT/.." ./gradlew :library:embedAndSignAppleFrameworkForXcode
- SwiftUI Usage: Load it using
UIViewControllerRepresentable:import SwiftUI import library // Your KMP framework name struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } struct ContentView: View { var body: some View { ComposeView() .ignoresSafeArea(.all) } }
In your desktop app entry point:
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import myproject.EditPhotoScreen
fun main() = application {
val windowState = rememberWindowState(width = 800.dp, height = 750.dp)
Window(
onCloseRequest = ::exitApplication,
title = "Image Editor",
state = windowState
) {
EditPhotoScreen()
}
}| Parameter | Type | Description |
|---|---|---|
image |
ImageBitmap |
The source image to be edited and cropped. |
modifier |
Modifier |
Layout modifier for the overall screen container. |
onCropSuccess |
(ImageBitmap) -> Unit |
Callback triggered when the checkmark (tick) button is clicked. Returns the cropped ImageBitmap (auto-scaled under 1 MB). |
onCancel |
() -> Unit |
Callback triggered when the Close ("X") button is clicked. |
