BelZSpeedScan is a lightweight and easy-to-use library for scanning QR codes and barcodes. It supports both Kotlin Multiplatform (KMP) and native Android development, providing a consistent API across platforms. This allows you to use the same scanning logic in your shared KMP code and seamlessly integrate it into your Android application.
- 📱 Cross-platform support (Android & iOS)
- 🚀 High-performance scanning using MLKit
- 🎨 Customizable UI components
- 🔒 Security alerts for suspicious codes
- 🔊 Audio feedback on successful scans
- 🎯 Multiple barcode formats support
- ⚡ Real-time scanning
- 🛠️ Easy integration with KMP projects
Android | iOS |
---|---|
![]() |
![]() |
Add the BelZSpeedScan dependency to your commonMain
source set in your project's build.gradle
file:
repositories {
mavenCentral()
}
dependencies {
commonMain {
implementation("io.github.ismoy:belzspeedscan:1.0.11") // Replace with the actual version
}
// ... other dependencies
}
For native Android development, include the dependency in your module's build.gradle file:
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.ismoy:belzspeedscan:1.0.11") // Replace with the actual version
// ... other dependencies
}
For detailed documentation and examples, please visit our documentation site.
We love your input! We want to make contributing to BelZSpeedScan as easy and transparent as possible, whether it's:
- Reporting a bug
- Discussing the current state of the code
- Submitting a fix
- Proposing new features
- Becoming a maintainer
Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
Check out our Roadmap to see what's coming next and how you can help!
Please report any bugs or issues you find in the issues section.
This project is licensed under the MIT License - see the LICENSE file for details.
- Thanks to all our contributors
- Special thanks to the MLKit team for their amazing work
- The Kotlin Multiplatform community for their support
- GitHub Issues: Create an issue
- Email: [email protected]
Give a ⭐️ if this project helped you!
import io.github.ismoy.belzspeedscan.domain.CodeScanner // Import CodeScanner
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLifecycleOwner
fun App(context: Any? = null) {
CameraScreen(context)
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App(this)
}
}
}
<key>NSCameraUsageDescription</key>
<string>Necesitamos acceso a la cámara para escanear códigos QR y códigos de barras</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
@Composable
fun CameraManagerUtils(
context: Any?,
onCodeScanned: (String) -> Unit
) {
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
var hasCameraPermission by remember { mutableStateOf(false) }
val scanner: CodeScanner? by remember { mutableStateOf(null) }
DisposableEffect(Unit) {
onDispose {
scanner?.stopScanning()
}
}
RequestCameraPermission { granted ->
hasCameraPermission = granted
}
var securityAlertVisible by remember { mutableStateOf(false) }
var securityAlertMessage by remember { mutableStateOf("") }
Scaffold(
content = { innerPadding ->
if (hasCameraPermission) {
Box(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
) {
Box {
var currentScanner by remember { mutableStateOf<CodeScanner?>(null) }
CameraPreview(
onPreviewViewReady = { preview ->
currentScanner = createBelSpeedScanCodeScanner(
context = context,
lifecycleOwner = lifecycleOwner,
previewView = preview,
playSound = true,
resourceName = "beep",
resourceExtension = "mp3",
delayToNextScan = 3000,
onCodeScanned = { scannedText ->
onCodeScanned(scannedText)
},
onSecurityAlert = {securityAlertInfo->
securityAlertMessage = "${securityAlertInfo.message}\n${securityAlertInfo.codeValue}\nRazón: ${securityAlertInfo.reason}"
securityAlertVisible = true
}
).also {
it.startScanning()
}
},
scanner = currentScanner,
modifier = Modifier.fillMaxHeight(1F),
)
}
if (securityAlertVisible) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
CustomTooltip(
icon = Icons.Filled.Warning,
text = securityAlertMessage,
bottomImage = HorizontalLinePainter(),
modifier = Modifier
.fillMaxWidth(0.9f)
)
}
}
GlobalScope.launch {
delay(2000)
securityAlertVisible = false
}
}
} else {
Box(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight(1F)
.padding(innerPadding)
.background(Color.Black.copy(alpha = 0.8f))
)
}
}
)
}
fun CameraScreen(context: Any?) {
CameraManagerUtils(context) { codeScanned ->
// Scan result
}
}
RequestCameraPermission { granted ->
hasCameraPermission = granted
}
RequestCameraPermission(
titleDialogConfig = "Your app needs camera permission",
descriptionDialogConfig = "Your app needs camera permission to scan QR codes",
btnDialogConfig = "Open Settings",
titleDialogDenied = "Camera permission denied",
descriptionDialogDenied = "You need to grant camera permission to scan QR codes",
btnDialogDenied = "Grant Permission",
customDeniedDialog = {
//Your compose custom dialog
},
customSettingsDialog = {
//Your compose custom dialog
}
) {granted->
println("CameraManagerUtils: $granted")
}
@Composable
fun CameraManagerUtils(
onCodeScanned: (String) -> Unit
) {
val context = LocalContext.current
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
var hasCameraPermission by remember { mutableStateOf(false) }
val scanner: CodeScanner? by remember { mutableStateOf(null) }
DisposableEffect(Unit) {
onDispose {
scanner?.stopScanning()
}
}
RequestCameraPermission { granted ->
hasCameraPermission = granted
}
Scaffold(
content = { innerPadding ->
if (hasCameraPermission) {
Box(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
) {
Box {
var currentScanner by remember { mutableStateOf<CodeScanner?>(null) }
CameraPreview(
onPreviewViewReady = { preview ->
currentScanner = createBelSpeedScanCodeScanner(
context = context,
lifecycleOwner = lifecycleOwner,
previewView = preview,
playSound = true,
resourceName = "sounds",
resourceExtension = "mp3",
delayToNextScan = 1000,
onCodeScanned = { scannedText ->
onCodeScanned(scannedText)
}
).also {
it.startScanning()
}
},
scanner = currentScanner,
modifier = Modifier.fillMaxHeight(1F),
waterMark = "",
tooFarText = "",
tooOptimalText = "",
)
}
}
} else {
Box(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight(1F)
.padding(innerPadding)
.background(Color.Black.copy(alpha = 0.8f))
)
}
}
)
}
fun CameraScreen() {
CameraManagerUtils() { codeScanned ->
// Scan Result
}
}
BelZSpeedScan handles camera permissions automatically on Android. However, on iOS, you need to add the camera usage description to your Info.plist
file.
If you don't have an Info.plist
file, you need to create it. Then, go to composeApp/iosMain/iosApp/iosApp/Info.plist
and add the following:
<key>NSCameraUsageDescription</key>
<string>We need access to the camera to scan QR codes and barcodes.</string>
If you need emit a sound when the code scan., go to composeApp/iosMain/resources/beep.mp3
remember respect this exactly name resources/beep.mp3
- Imports: Import necessary classes, including CodeScanner from the library.
- Function: This function serves as the entry point for using the scanner. The context parameter is crucial for Android integration.
• scannedCode: Stores the result of the scan.
• hasCameraPermission: Tracks whether the user has granted camera permission.
• scanner: Holds an instance of the CodeScanner.
- RequestCameraPermission: This function handles requesting camera permissions. The default implementation provides a standard dialog. You can customize the dialogs by using the customDeniedDialog and customSettingsDialog parameters. The onResult lambda provides a boolean indicating whether permission was granted.
- Conditional Rendering: The if (hasCameraPermission) block ensures that the camera preview and scanner are only initialized if permission has been granted.
- CameraPreview: This composable displays the camera preview. The onPreviewViewReady lambda is called when the PreviewView is ready, allowing you to initialize the CodeScanner.
- createBelSpeedScanCodeScanner: This function creates an instance of the CodeScanner. It takes the context, LifecycleOwner, PreviewView, isQRScanning (true for QR code scanning only, false for barcode scanning only. No default value), playSound, and a lambda for handling the scanned code.
- startScanning(): Starts the scanning process.
- Scanned Code Handling: The lambda passed to createBelSpeedScanCodeScanner is called when a code is scanned. The scannedCode parameter contains the scanned data.
Good news! Native iOS support is on the way. We're working hard to bring the functionality of BelZSpeedScan to the iOS platform, allowing you to use the same scanning logic in your iOS applications. Stay tuned for future updates and announcements regarding the availability of iOS support.