Create a barcode scanner with Jetpack Compose

HariAgusWidakdo
3 min read5 days ago

--

I will try to make a scanner with Jetpack Compose. previously, there was something that needed to be considered to create a barcode scanner, namely some of the libraries needed like mlkit-barcode, androidx-camera.

Ok let’s go, we can create a new project with Android Studio, then we can add in the library below in build.gradle :

dependencies {
implementation "androidx.camera:camera-camera2:1.1.0"
implementation "androidx.camera:camera-lifecycle:1.1.0"
implementation "androidx.camera:camera-view:1.0.0"
implementation "com.google.mlkit:barcode-scanning:17.1.0"
implementation "androidx.compose.ui:ui:1.4.0"
implementation "androidx.compose.material:material:1.4.0"
implementation "androidx.compose.ui:ui-tooling-preview:1.4.0"
implementation "androidx.activity:activity-compose:1.6.1"
}

Next we need to add permissions to access the camera, we can add it to the file AndroidManifest.xml :

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />

NB : If the target SDK is Android 6.0 (API level 23) or higher, you need to request runtime persmissions for camera access

Next we will create the UI for the display and also the scanning

@Composable
fun ScannerScreen(onBarcodeDetected: (String) -> Unit) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }

val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
val preview = Preview.Builder().build()
val barcodeScanner = BarcodeScanner.getClient()

Box(modifier = Modifier.fillMaxSize()) {
// Camera Preview
CameraPreview(cameraProviderFuture = cameraProviderFuture, cameraSelector = cameraSelector, preview = preview)

// Display barcode scan result
Text(
text = "Scanning...",
modifier = Modifier.align(Alignment.Center),
style = TextStyle(color = Color.White, fontSize = 24.sp)
)
}

// Process barcode detection
LaunchedEffect(key1 = cameraProviderFuture) {
val cameraProvider = cameraProviderFuture.get()

val imageAnalysis = ImageAnalysis.Builder()
.build()

imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(context)) { imageProxy ->
processImage(imageProxy, barcodeScanner, onBarcodeDetected)
}

cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageAnalysis
)
}
}

Where are the CameraPreview and processImage codes?

We will use the processImage function to handle when success or error occurs during barcode capture.

fun processImage(imageProxy: ImageProxy, barcodeScanner: BarcodeScanner, onBarcodeDetected: (String) -> Unit) {
val image = InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees)
barcodeScanner.process(image)
.addOnSuccessListener { barcodes ->
if (barcodes.isNotEmpty()) {
val barcode = barcodes.first()
val barcodeValue = barcode.displayValue
onBarcodeDetected(barcodeValue ?: "Unknown")
}
}
.addOnFailureListener {
// Handle failure
}
.addOnCompleteListener {
imageProxy.close()
}
}

For the CameraPreview function to display the camera result

@Composable
fun CameraPreview(cameraProviderFuture: androidx.concurrent.futures.CallbackToFutureAdapter, cameraSelector: CameraSelector, preview: Preview) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current

val previewView = remember { PreviewView(context) }

LaunchedEffect(key1 = cameraProviderFuture) {
val cameraProvider = cameraProviderFuture.get()

// Bind camera to lifecycle
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview
)

preview.setSurfaceProvider(previewView.surfaceProvider)
}

AndroidView(factory = { previewView })
}

Done, if the barcode is detected, we can process it by implementing a function thath display the scan result in the UI :

@Composable
fun ScannerResult(result: String) {
Text(
text = "Scan Result: $result",
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
style = MaterialTheme.typography.h6
)
}

@Composable
fun ScannerApp() {
var scannedResult by remember { mutableStateOf("") }

ScannerScreen(onBarcodeDetected = {
scannedResult = it
})

if (scannedResult.isNotEmpty()) {
ScannerResult(result = scannedResult)
}
}

Done now we can run the app. Thanks for reading 🚀🔥

--

--

HariAgusWidakdo
HariAgusWidakdo

Written by HariAgusWidakdo

Mobile Developer | Kotlin | SwiftUI 📱. Sometimes write my story 📚

No responses yet