Compose Multiplatform Barcode Scanning Library
| Android | iOS |
|---|---|
|
|
|
KScan is a Compose Multiplatform library that makes it easy to scan barcodes in your apps
To integrate KScan into your project
Add JitPack repository in your settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://jitpack.io")
}
}Add the dependency in your common module's commonMain source set:
implementation("com.github.Dong-Lin:KScan:0.3.2")implementation("io.github.Dong-Lin:KScan:0.3.2")Note: This is a fork of ismai117/KScan with added ROI support. See SETUP.md for detailed integration instructions.
Android - MLKit
- Uses Google’s MLKit library for barcode scanning on Android
iOS - AVFoundation
- Utilizes Apple’s AVFoundation framework for camera setup and barcode scanning on iOS
Important: iOS requires you to add the "Privacy - Camera Usage Description" key to your Info.plist file inside xcode, you need to provide a reason for why you want to access the camera.
Basic Usage
To use KScan, simply add the ScannerView in your app like this:
if (showScanner) {
ScannerView(
codeTypes = listOf(
BarcodeFormats.FORMAT_QR_CODE,
BarcodeFormats.FORMAT_EAN_13,
)
) { result ->
when (result) {
is BarcodeResult.OnSuccess -> {
println("Barcode: ${result.barcode.data}, format: ${result.barcode.format}")
}
is BarcodeResult.OnFailed -> {
println("error: ${result.exception.message}")
}
BarcodeResult.OnCanceled -> {
println("scan canceled")
}
}
}
}To dismiss the scanner, you need to manage your own state, set it to false in the right places inside the ScannerView block after you handle the results
if (showScanner) {
ScannerView(
codeTypes = listOf(
BarcodeFormats.FORMAT_QR_CODE,
BarcodeFormats.FORMAT_EAN_13,
)
) { result ->
when (result) {
is BarcodeResult.OnSuccess -> {
println("Barcode: ${result.barcode.data}, format: ${result.barcode.format}")
showScanner = false
}
is BarcodeResult.OnFailed -> {
println("Error: ${result.exception.message}")
showScanner = false
}
BarcodeResult.OnCanceled -> {
showScanner = false
}
}
}
}If you want to remove the UI and just use the raw scanner, you can set the showUi parameter to false
if (showScanner) {
ScannerView(
codeTypes = listOf(
BarcodeFormats.FORMAT_QR_CODE,
BarcodeFormats.FORMAT_EAN_13,
),
showUi = false
) { result ->
when (result) {
is BarcodeResult.OnSuccess -> {
println("Barcode: ${result.barcode.data}, format: ${result.barcode.format}")
showScanner = false
}
is BarcodeResult.OnFailed -> {
println("Error: ${result.exception.message}")
showScanner = false
}
BarcodeResult.OnCanceled -> {
showScanner = false
}
}
}
}Region of Interest (ROI)
You can restrict barcode scanning to a specific region of the camera preview using scanRegion. This improves performance and accuracy by only detecting barcodes within the defined area:
// Define a centered scan region (60% width, 40% height)
val scanRegion = ScanRegion.centered(
width = 0.6f,
height = 0.4f
)
if (showScanner) {
ScannerView(
codeTypes = listOf(BarcodeFormats.FORMAT_QR_CODE),
scanRegion = scanRegion // Only detect barcodes in this region
) { result ->
// Handle result
}
}You can also create custom regions:
// Custom region: top-right corner
val customRegion = ScanRegion(
left = 0.5f, // Start at 50% from left
top = 0.1f, // Start at 10% from top
width = 0.4f, // 40% width
height = 0.3f // 30% height
)When a scan region is specified, the UI will automatically show a visual overlay highlighting the active scanning area.
Custom Scanner UI
To build a custom scanner UI with torch and zoom control, set showUi = false and use a ScannerController.
val scannerController = remember { ScannerController() }
if (showScanner) {
ScannerView(
codeTypes = listOf(BarcodeFormat.FORMAT_ALL_FORMATS),
showUi = false,
scannerController = scannerController
) { result ->
when (result) {
is BarcodeResult.OnSuccess -> {
println("Barcode: ${result.barcode.data}, format: ${result.barcode.format}")
showScanner = false
}
is BarcodeResult.OnFailed -> {
println("Error: ${result.exception.message}")
showScanner = false
}
BarcodeResult.OnCanceled -> {
showScanner = false
}
}
}
Column(
modifier = Modifier.align(Alignment.BottomCenter).padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
scannerController.setTorch(!scannerController.torchEnabled)
}) {
Text("Torch ${if (scannerController.torchEnabled) "Off" else "On"}")
}
Slider(
value = scannerController.zoomRatio,
onValueChange = scannerController::setZoom,
valueRange = 1f..scannerController.maxZoomRatio
)
}
}If you’d like to contribute, whether it’s fixing bugs, improving documentation, adding features, or helping with maintenance, your support is greatly appreciated!