DEV Community

Cover image for QRKit — QRCode Scanning in Compose Multiplatform for Android and iOS
Mobile innovation Network
Mobile innovation Network

Posted on

2

QRKit — QRCode Scanning in Compose Multiplatform for Android and iOS

QRKit is a Kotlin Multiplatform library for Qr Scan in your Android or iOS App.

Installation

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

commonMain.dependencies {
    implementation("network.chaintech:qr-kit:1.0.2")
}
Enter fullscreen mode Exit fullscreen mode

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

Example

QrScanner(
    modifier: Modifier,
    flashlightOn: Boolean,
    launchGallery: Boolean,
    onCompletion: (String) -> Unit,
    onGalleryCallBackHandler: (Boolean) -> Unit,
    onFailure: (String) -> Unit
)
Enter fullscreen mode Exit fullscreen mode
  • modifier: Modifier for modifying the layout of the QR scanner.
  • flashlightOn: Boolean indicating whether the flashlight is turned on.
  • launchGallery: Boolean indicating whether to launch the gallery for selecting images.
  • onCompletion: Callback invoked when a QR code is successfully scanned.
  • onGalleryCallBackHandler: 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 launchGallery 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 = Modifier
                            .size(250.dp)
                            .clip(shape = RoundedCornerShape(size = 14.dp))
                            .clipToBounds()
                            .border(2.dp, Color.Gray, RoundedCornerShape(size = 14.dp)),
                        contentAlignment = Alignment.Center
                    ) {
                        QrScanner(
                            modifier = Modifier
                                .clipToBounds()
                                .clip(shape = RoundedCornerShape(size = 14.dp)),
                            flashlightOn = flashlightOn,
                            launchGallery = launchGallery,
                            onCompletion = {
                                qrCodeURL = it
                                startBarCodeScan = false
                            },
                            onGalleryCallBackHandler = {
                                launchGallery = it
                            },
                            onFailure = {
                                coroutineScope.launch {
                                    if (it.isEmpty()) {
                                        snackBarHostState.showSnackbar("Invalid qr code")
                                    } else {
                                        snackBarHostState.showSnackbar(it)
                                    }
                                }
                            }
                        )
                    }

                    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 {
                                        launchGallery = true
                                    }
                            )
                        }
                    }
                }
            } 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
            )
        }
    }

}
Enter fullscreen mode Exit fullscreen mode

Tech Stack

  • Compose Multiplatform
  • CameraX Jetpack library
  • ML Kit

Conclusion

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

https://github.com/ChainTechNetwork/QRKitComposeMultiplatform.git

Happy coding ❤

Connect with us 👇

Sentry blog image

The Visual Studio App Center’s retiring

But sadly….you’re not. See how to make the switch to Sentry for all your crash reporting needs.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs