QRKit — QRCode Scanning in Compose Multiplatform

ssvaghasiya
Mobile Innovation Network
3 min readApr 18, 2024

QRKit is a Kotlin Multiplatform library for Qr Scan in your Android, iOS & Desktop App.

Installation

Add the dependency to your build.gradle.kts file:

commonMain.dependencies {
implementation("network.chaintech:qr-kit:1.0.6")
}

QrScanner :- Add Permissions in Android and iOS

Android : Include this at root level in your AndroidManifest.xml:

<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
  • iOS : Add below key to the Info.plist in your xcode project:
<key>NSCameraUsageDescription</key><string>$(PRODUCT_NAME) camera description.</string>
<key>NSPhotoLibraryUsageDescription</key><string>$(PRODUCT_NAME)photos description.</string>

Example

QrScanner(
modifier: Modifier,
flashlightOn: Boolean,
openImagePicker: Boolean,
onCompletion: (String) -> Unit,
imagePickerHandler: (Boolean) -> Unit,
onFailure: (String) -> Unit
)
  • modifier: Modifier for modifying the layout of the QR scanner.
  • flashlightOn: Boolean indicating whether the flashlight is turned on.
  • openImagePicker: Boolean indicating whether to launch the picker for selecting images.
  • imagePickerHandler: Callback invoked to indicate the status of the gallery, whether it's open or closed.
  • onFailure: Callback invoked when there's a failure during QR code scanning.

Usage

@Composable
fun QrScannerCompose() {
var qrCodeURL by remember { mutableStateOf("") }
var startBarCodeScan by remember { mutableStateOf(false) }
var flashlightOn by remember { mutableStateOf(false) }
var openImagePicker by remember { mutableStateOf(value = false) }
val snackBarHostState = LocalSnackBarHostState.current
val coroutineScope = rememberCoroutineScope()

Box(modifier = Modifier.fillMaxSize().statusBarsPadding()) {
Column(
modifier = Modifier
.background(color = Color.White)
.windowInsetsPadding(WindowInsets.safeDrawing)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (qrCodeURL.isEmpty() && startBarCodeScan) {
Column(
modifier = Modifier
.background(color = Color.Black)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = if (platformName() != "Desktop") {
Modifier
.size(250.dp)
.clip(shape = RoundedCornerShape(size = 14.dp))
.clipToBounds()
.border(2.dp, Color.Gray, RoundedCornerShape(size = 14.dp))
} else {
Modifier
},
contentAlignment = Alignment.Center
) {
QrScanner(
modifier = Modifier
.clipToBounds()
.clip(shape = RoundedCornerShape(size = 14.dp)),
flashlightOn = flashlightOn,
openImagePicker = openImagePicker,
onCompletion = {
qrCodeURL = it
startBarCodeScan = false
},
imagePickerHandler = {
openImagePicker = it
},
onFailure = {
coroutineScope.launch {
if (it.isEmpty()) {
snackBarHostState.showSnackbar("Invalid qr code")
} else {
snackBarHostState.showSnackbar(it)
}
}
}
)
}

if (platformName() != "Desktop") {
Box(
modifier = Modifier
.padding(start = 20.dp, end = 20.dp, top = 30.dp)
.background(
color = Color(0xFFF9F9F9),
shape = RoundedCornerShape(25.dp)
)
.height(35.dp),
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier
.padding(vertical = 5.dp, horizontal = 18.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(11.dp)
) {
Icon(imageVector = if (flashlightOn) Icons.Filled.FlashOn else Icons.Filled.FlashOff,
"flash",
modifier = Modifier
.size(24.dp)
.clickable {
flashlightOn = !flashlightOn
})

VerticalDivider(
modifier = Modifier,
thickness = 1.dp,
color = Color(0xFFD8D8D8)
)

Image(
painter = painterResource(Res.drawable.ic_gallery_icon),
contentDescription = "gallery",
contentScale = ContentScale.Fit,
modifier = Modifier
.size(24.dp)
.clickable {
openImagePicker = true
}
)
}
}
} else {
Button(
modifier = Modifier.padding(top = 12.dp),
onClick = {
openImagePicker = true
},
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF007AFF)),
) {
Text(
text = "Select Image",
modifier = Modifier.background(Color.Transparent)
.padding(horizontal = 12.dp, vertical = 12.dp),
fontSize = 16.sp
)
}
}
}
} else {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(
onClick = {
startBarCodeScan = true
qrCodeURL = ""
},
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF007AFF)),
) {
Text(
text = "Scan Qr",
modifier = Modifier.background(Color.Transparent)
.padding(horizontal = 12.dp, vertical = 12.dp),
fontSize = 16.sp
)
}

Text(
text = qrCodeURL,
color = Color.Black,
modifier = Modifier.padding(top = 12.dp)
)
}
}
}
if (startBarCodeScan) {
Icon(
imageVector = Icons.Filled.Close,
"Close",
modifier = Modifier
.padding(top = 12.dp, end = 12.dp)
.size(24.dp)
.clickable {
startBarCodeScan = false
}.align(Alignment.TopEnd),
tint = Color.White
)
}
}
}

Tech Stack

  • Compose Multiplatform
  • CameraX Jetpack library
  • ML Kit

Used below library for image picker in QRKit

For QRCode Generator follow below link:

https://medium.com/mobile-innovation-network/qrkit-barcode-scanning-in-compose-multiplatform-for-android-and-ios-77cf5d84f719

Conclusion

Integrating a QR code scanner library enhances functionality, streamlines processes, and improves user experience in your application.

Happy coding ❤

--

--