
Lightweight library for scanning QR codes and barcodes, leveraging MLKIT for decoding. Features consistent API, seamless integration, and haptic feedback for enhanced user experience.
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.
| 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.10") // 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
}The core functionality of BelZSpeedScan is accessed through the App function (or similar entry point in your KMP project). This function requires a context parameter, which in Android would typically be your MainActivity's context.
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
• scannedCode: Stores the result of the scan.
• hasCameraPermission: Tracks whether the user has granted camera permission.
• scanner: Holds an instance of the CodeScanner.
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.
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.
| 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.10") // 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
}The core functionality of BelZSpeedScan is accessed through the App function (or similar entry point in your KMP project). This function requires a context parameter, which in Android would typically be your MainActivity's context.
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
• scannedCode: Stores the result of the scan.
• hasCameraPermission: Tracks whether the user has granted camera permission.
• scanner: Holds an instance of the CodeScanner.
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.