Introduction
By using a cross-platform framework to create your barcode scanning app, you only need to maintain a single codebase for multiple target platforms, e.g., iOS and Android. In this tutorial, we’ll show you how to build a cross-platform barcode scanner using four of the most popular mobile development frameworks: React Native, Flutter, .NET MAUI, and Capacitor.
After completing the tutorial, you’ll be able to scan any barcode with your smartphone, both one at a time and many at once.


If you’re interested in a particular framework, you can jump straight to it:
Let’s get started!
React Native
Requirements
Before starting app development with React Native, you need to set up your local development environment. A real device is needed to get the full benefits from using our SDK. The steps for preparing your local environment for development can be found in the Expo documentation.
💡 Starting with React Native version 0.75, it’s recommended to use frameworks such as Expo to develop React Native applications. We’ll use Expo in this tutorial as well.
Of course, you can also integrate our SDK without a framework. For further information, please refer to the official React Native documentation.
1. Preparing the project
First, let’s create our Expo app using the Expo CLI, which will walk us through the setup process. To create an Expo app, run the following command in the terminal:
npx create-expo-app@latest
Let’s open the project with your favorite code editor and prepare it to install the Scanbot SDK.
Remove boilerplate code
The Expo CLI has generated some screens to ensure the app isn’t empty. We can remove the generated code quickly by running the following command:
npm run reset-project
Now the app directory only contains an index file, which is our only screen, and a _layout.tsx
file.
⚠️ Npm is the default package manager in this project, so we’ll be using it for this tutorial. Feel free to set up the project with other package managers supported by Expo.
Generate the native projects
To install expo-dev-client
, we need to run the following command:
npx expo install expo-dev-client
After that, we generate our iOS and Android projects with:
npx expo prebuild
⚠️ If you are using a React Native Barcode Scanner SDK trial license, make sure that the Android applicationId
and iOS bundle identifier are the same.
2. Installing the SDK
Let’s install and configure the React Native Barcode Scanner SDK.
First we need to run:
npx expo install react-native-scanbot-barcode-scanner-sdk
Now that the npm package has been installed, all that’s left is to make the necessary native changes to the projects.
You can use our config plugin or manually configure the native projects. We’ll showcase both so you can pick the method you prefer.
Method A: Expo config plugin
To utilize the plugin, add the following to your app.json file:
"plugins": [
"expo-router",
[
"react-native-scanbot-barcode-scanner-sdk",
{
"iOSCameraUsageDescription": "Camera permission is needed to scan barcodes",
"androidCameraPermission": true,
"androidCameraFeature": true,
"mavenURLs": true
}
]
],
Then run:
npx expo prebuild
Now we’re all set. You can skip the Android and iOS native changes when using this plugin.
Method B: Manual configuration
Alternatively, you can also apply the changes to the native projects manually.
Android
For Android, we first need to add the camera permissions in android/app/src/main/AndroidManifest.xml.
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
For development builds, we also need to add our Maven package URLs in android/build.gradle.
allprojects {
repositories {
... other maven rpositories
maven { url "https://nexus.scanbot.io/nexus/content/repositories/releases/" }
maven { url "https://nexus.scanbot.io/nexus/content/repositories/snapshots/" }
}
}
💡 For more information about individual releases, please refer to our changelog.
iOS
For iOS, we need to include a description for the camera permission in ios/{projectName}/Info.plist anywhere inside the element:
<key>NSCameraUsageDescription</key>
<string>Camera permission is needed to scan barcodes</string>
Now that the project is set up, we can start integrating the barcode scanning functionalities.
3. Initializing the SDK
Before using any feature of the React Native Barcode Scanner SDK, we need to initialize it. Ideally, initialization should be done as soon as the app is launched.
There are many ways to initialize the SDK that depend on your use case. In our example, we’re going to initialize the SDK inside a useEffect
in our _layout.tsx file.
ScanbotBarcodeSDK
.initializeSdk({ licenseKey: "" })
.then(result => console.log(result))
.catch(err => console.log(err));
So the _layout.tsx would look like this:
import { Stack } from "expo-router";
import { useEffect } from "react";
import ScanbotBarcodeSDK from "react-native-scanbot-barcode-scanner-sdk"
export default function RootLayout() {
useEffect(() => {
ScanbotBarcodeSDK
.initializeSdk({ licenseKey: "" })
.then(result => console.log(result))
.catch(err => console.log(err));
}, []);
return (
<Stack>
<Stack.Screen name="index" />
</Stack>
);
}
💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key using the bundle and application identifiers.
4. Implementing the scanning modes
Our RTU UI components make it easy to deploy our Barcode Scanner SDK’s different scanning modes in your app. Let’s start with the simplest use case: single-barcode scanning.
In app/index.tsx, we’re going to add a button that will start the scanning process. After we ran the reset project script, the index.tsx should only contain a single view with a text component. Let’s replace the Text
component with a button component like this:
<Button title={"Start single barcode scanning"} onPress={onSingleBarcodeScan} />
Now we need to define onBarcodeScannerPress
, which will start the single-barcode scanning mode:
const onSingleBarcodeScan = useCallback(async () => {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for single scanning */
config.useCase = new SingleScanningMode();
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result if result status is OK */
if (result.status === 'OK' && result.data) {
Alert.alert(
"Barcode Scanning successfully!",
`${result.data.items.map(barcode =>
`Barcode value: ${barcode.text} and type: ${barcode.type}`
).join("\n")}`);
} else {
console.log("The user has canceled the Barcode Scanning")
}
} catch (e: any) {
console.log("An error has occurred while running Barcode Scanner", e.message);
}
}, []);
Our final screen should look like this:
import {Alert, Button, View} from "react-native";
import {useCallback} from "react";
import {
BarcodeScannerConfiguration,
SingleScanningMode,
startBarcodeScanner
} from "react-native-scanbot-barcode-scanner-sdk/ui_v2";
import ScanbotBarcodeSDK from "react-native-scanbot-barcode-scanner-sdk";
export default function Index() {
const onSingleBarcodeScan = useCallback(async () => {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for single scanning */
config.useCase = new SingleScanningMode();
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result if result status is OK */
if (result.status === 'OK' && result.data) {
Alert.alert(
"Barcode Scanning successfully!",
`${result.data.items.map(barcode =>
`Barcode value: ${barcode.text} and type: ${barcode.type}`
).join("\n")}`);
} else {
console.log("The user has canceled the Barcode Scanning")
}
} catch (e: any) {
console.log("An error has occurred while running Barcode Scanner", e.message);
}
}, []);
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Button title={"Start single barcode scanning"} onPress={onSingleBarcodeScan}/>
</View>
);
}
Now we’re ready to start building our app and scan some barcodes. Let’s also add the multi-barcode scanning and AR overlay modes in the same way:
const onMultiBarcodeScan = useCallback(async () => {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for multi-scanning */
config.useCase = new MultipleScanningMode();
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result if result status is OK */
if (result.status === 'OK' && result.data) {
Alert.alert(
"Barcode Scanning successfully!",
`${result.data.items.map(barcode =>
`Barcode value: ${barcode.text} and type: ${barcode.type}`
).join("\n")}`);
} else {
console.log("The user has canceled the Barcode Scanning")
}
} catch (e: any) {
console.log("An error has occurred while running Barcode Scanner", e.message);
}
}, []);
const onAROverlayBarcodeScan = useCallback(async () => {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for multi-scanning */
config.useCase = new MultipleScanningMode();
/** Configure AR Overlay. */
config.useCase.arOverlay.visible = true;
config.useCase.arOverlay.automaticSelectionEnabled = false;
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result if result status is OK */
if (result.status === 'OK' && result.data) {
Alert.alert(
"Barcode Scanning successfully!",
`${result.data.items.map(barcode =>
`Barcode value: ${barcode.text} and type: ${barcode.type}`
).join("\n")}`);
} else {
console.log("The user has canceled the Barcode Scanning")
}
} catch (e: any) {
console.log("An error has occurred while running Barcode Scanner", e.message);
}
}, []);
Finally, we need to add the buttons to start the scanning modes:
<Button title={"Start multi-barcode scanning"} onPress={onMultiBarcodeScan}/>
<Button title={"Start AR Overlay barcode scanning"} onPress={onAROverlayBarcodeScan}/>
5. Building the app
We’re all done setting up our app.
To run the app on your Android or iOS device, use the following commands:
For Android:
npx expo run:android --device
For iOS:
npx expo run:ios --device
Flutter
Requirements
To develop Flutter applications for Android and iOS, ensure you have the following:
- Flutter SDK: Download the latest version for your OS.
- Dart SDK: No separate installation needed, since it’s integrated with Flutter.
- The right IDEs for your target platforms:
- Android Studio to develop for Android. It includes the Android SDK and AVD Manager.
- Xcode for iOS development on macOS.
- You can also use Visual Studio Code, which supports Flutter plugins.
⚠️ A note to Windows users: A Mac is necessary to build and test iOS apps. Consider using a remote Mac service or a physical Mac as a build server.
1. Preparing the project
First, create a new directory for your project and navigate into it:
mkdir test-flutter
cd test-flutter
Create a new Flutter project:
flutter create test_barcode_scanner
cd test_barcode_scanner
Since we need access to the device camera to scan barcodes, let’s add the necessary camera permissions for Android and iOS.
Add camera permissions to android/app/src/main/AndroidManifest.xml
:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" />
And in ios/Runner/Info.plist
:
<key>NSCameraUsageDescription</key>
<string>We need camera access to scan barcodes.</string>
2. Installing the SDK
Add the Scanbot Flutter Barcode Scanner package to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
barcode_scanner: ^5.1.0
Run the following command to install the packages:
flutter pub get
💡 Please refer to our changelog for the latest version of the Barcode Scanner SDK.
Now that the project is set up, we can integrate the barcode scanning functionalities.
3. Initializing the SDK
Initialize the SDK in main.dart
:
import 'package:flutter/material.dart';
import 'package:barcode_scanner/scanbot_barcode_sdk_v2.dart';
const BARCODE_SDK_LICENSE_KEY = "";
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
_initScanbotSdk();
}
Future<void> _initScanbotSdk() async {
var config = ScanbotSdkConfig(
licenseKey: BARCODE_SDK_LICENSE_KEY,
loggingEnabled: true,
);
try {
await ScanbotBarcodeSdk.initScanbotSdk(config);
print('Scanbot SDK initialized successfully');
} catch (e) {
print('Error initializing Scanbot SDK: $e');
}
}
@override
Widget build(BuildContext context) {
...
}
💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key using your applicationId.
4. Implementing the scanning modes
The SDK’s RTU UI components make it easy to deploy our Flutter Barcode Scanner package’s different scanning modes in your app. Let’s start with the simplest use case: single-barcode scanning.
Implement the barcode scanning functionality:
void _startBarcodeScanning() async {
try {
// Create the default configuration object.
var configuration = BarcodeScannerConfiguration();
// Initialize the use case for single scanning.
var scanningMode = SingleScanningMode();
// Enable and configure the confirmation sheet.
scanningMode.confirmationSheetEnabled = true;
scanningMode.sheetColor = ScanbotColor("#FFFFFF");
// Hide/unhide the barcode image.
scanningMode.barcodeImageVisible = true;
// Configure the barcode title of the confirmation sheet.
scanningMode.barcodeTitle.visible = true;
scanningMode.barcodeTitle.color = ScanbotColor("#000000");
// Configure the barcode subtitle of the confirmation sheet.
scanningMode.barcodeSubtitle.visible = true;
scanningMode.barcodeSubtitle.color = ScanbotColor("#000000");
// Configure the cancel button of the confirmation sheet.
scanningMode.cancelButton.text = "Close";
scanningMode.cancelButton.foreground.color = ScanbotColor("#C8193C");
scanningMode.cancelButton.background.fillColor = ScanbotColor("#00000000");
// Configure the submit button of the confirmation sheet.
scanningMode.submitButton.text = "Submit";
scanningMode.submitButton.foreground.color = ScanbotColor("#FFFFFF");
scanningMode.submitButton.background.fillColor = ScanbotColor("#C8193C");
// Configure other parameters, pertaining to single-scanning mode as needed.
configuration.useCase = scanningMode;
// Set an array of accepted barcode types.
// configuration.recognizerConfiguration.barcodeFormats = [
// scanbotV2.BarcodeFormat.AZTEC,
// scanbotV2.BarcodeFormat.PDF_417,
// scanbotV2.BarcodeFormat.QR_CODE,
// scanbotV2.BarcodeFormat.MICRO_QR_CODE,
// scanbotV2.BarcodeFormat.MICRO_PDF_417,
// scanbotV2.BarcodeFormat.ROYAL_MAIL,
/// .....
// ];
// Configure other parameters as needed.
var result = await ScanbotBarcodeSdk.startBarcodeScanner(configuration);
if(result.operationResult == OperationResult.SUCCESS)
{
// TODO: present barcode result as needed
print(result.value?.items.first.type?.name);
}
} catch (e) {
print('Error: $e');
}
}
Now let’s go to the build
method and add the button on the start screen so it calls our _startBarcodeScanning
method when clicked:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Barcode Scanner'),
),
body: Center(
child: ElevatedButton(
onPressed: _startBarcodeScanning,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start single-barcode scanning"),
),
),
);
}
If you like, you can build and run the app to see if everything is working correctly so far:
flutter run

In the next step, we’re going to insert both the multi-barcode scanning and the AR overlay modules.
Let’s create two more methods, _startBarcodeMultiScanning
and _startAROverlay
:
void _startBarcodeMultiScanning() async {
try {
// Create the default configuration object.
var configuration = BarcodeScannerConfiguration();
// Initialize the use case for multiple scanning.
var scanningMode = MultipleScanningMode();
// Set the counting mode.
scanningMode.mode = MultipleBarcodesScanningMode.COUNTING;
// Set the sheet mode for the barcodes preview.
scanningMode.sheet.mode = SheetMode.COLLAPSED_SHEET;
// Set the height for the collapsed sheet.
scanningMode.sheet.collapsedVisibleHeight = CollapsedVisibleHeight.LARGE;
// Enable manual count change.
scanningMode.sheetContent.manualCountChangeEnabled = true;
// Set the delay before same barcode counting repeat.
scanningMode.countingRepeatDelay = 1000;
// Configure the submit button.
scanningMode.sheetContent.submitButton.text = "Submit";
scanningMode.sheetContent.submitButton.foreground.color =
ScanbotColor("#000000");
// Configure other parameters, pertaining to multiple-scanning mode as needed.
configuration.useCase = scanningMode;
// Set an array of accepted barcode types.
// configuration.recognizerConfiguration.barcodeFormats = [
// BarcodeFormat.AZTEC,
// BarcodeFormat.PDF_417,
// BarcodeFormat.QR_CODE,
// BarcodeFormat.MICRO_QR_CODE,
// BarcodeFormat.MICRO_PDF_417,
// BarcodeFormat.ROYAL_MAIL,
/// .....
// ];
// Configure other parameters as needed.
var result = await ScanbotBarcodeSdk.startBarcodeScanner(configuration);
if(result.operationResult == OperationResult.SUCCESS)
{
// TODO: present barcode result as needed
print(result.value?.items.first.type?.name);
}
} catch (e) {
print('Error: $e');
}
}
void _startAROverlay() async {
try {
// Create the default configuration object.
var configuration = new BarcodeScannerConfiguration();
// Configure the usecase.
var usecase = new MultipleScanningMode();
usecase.mode = MultipleBarcodesScanningMode.UNIQUE;
usecase.sheet.mode = SheetMode.COLLAPSED_SHEET;
usecase.sheet.collapsedVisibleHeight = CollapsedVisibleHeight.SMALL;
// Configure AR Overlay.
usecase.arOverlay.visible = true;
usecase.arOverlay.automaticSelectionEnabled = false;
// Set the configured usecase.
configuration.useCase = usecase;
// Set an array of accepted barcode types.
// configuration.recognizerConfiguration.barcodeFormats = [
// BarcodeFormat.AZTEC,
// BarcodeFormat.PDF_417,
// BarcodeFormat.QR_CODE,
// BarcodeFormat.MICRO_QR_CODE,
// BarcodeFormat.MICRO_PDF_417,
// BarcodeFormat.ROYAL_MAIL,
/// .....
// ];
var result = await ScanbotBarcodeSdk.startBarcodeScanner(configuration);
if (result.operationResult == OperationResult.SUCCESS) {
// TODO: present barcode result as needed
print(result.value?.items.first.type?.name);
}
} catch (e) {
print('Error: $e');
}
}
And let’s not forget to insert the buttons:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Barcode Scanner'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _startBarcodeScanning,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start single-barcode scanning"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _startBarcodeMultiScanning,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start multi-barcode scanning"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _startAROverlay,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start the AR overlay"),
),
],
),
),
);
}
Your final main.dart
should look something like this:
import 'package:flutter/material.dart';
import 'package:barcode_scanner/scanbot_barcode_sdk_v2.dart';
const BARCODE_SDK_LICENSE_KEY = "";
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
_initScanbotSdk();
}
Future<void> _initScanbotSdk() async {
var config = ScanbotSdkConfig(
licenseKey: BARCODE_SDK_LICENSE_KEY,
loggingEnabled: true,
);
try {
await ScanbotBarcodeSdk.initScanbotSdk(config);
print('Scanbot SDK initialized successfully');
} catch (e) {
print('Error initializing Scanbot SDK: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Barcode Scanner'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _startBarcodeScanning,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start single-barcode scanning"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _startBarcodeMultiScanning,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start multi-barcode scanning"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _startAROverlay,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Start the AR overlay"),
),
],
),
),
);
}
void _startBarcodeScanning() async {
try {
// Create the default configuration object.
var configuration = BarcodeScannerConfiguration();
// Initialize the use case for single scanning.
var scanningMode = SingleScanningMode();
// Enable and configure the confirmation sheet.
scanningMode.confirmationSheetEnabled = true;
scanningMode.sheetColor = ScanbotColor("#FFFFFF");
// Hide/unhide the barcode image.
scanningMode.barcodeImageVisible = true;
// Configure the barcode title of the confirmation sheet.
scanningMode.barcodeTitle.visible = true;
scanningMode.barcodeTitle.color = ScanbotColor("#000000");
// Configure the barcode subtitle of the confirmation sheet.
scanningMode.barcodeSubtitle.visible = true;
scanningMode.barcodeSubtitle.color = ScanbotColor("#000000");
// Configure the cancel button of the confirmation sheet.
scanningMode.cancelButton.text = "Close";
scanningMode.cancelButton.foreground.color = ScanbotColor("#C8193C");
scanningMode.cancelButton.background.fillColor = ScanbotColor("#00000000");
// Configure the submit button of the confirmation sheet.
scanningMode.submitButton.text = "Submit";
scanningMode.submitButton.foreground.color = ScanbotColor("#FFFFFF");
scanningMode.submitButton.background.fillColor = ScanbotColor("#C8193C");
// Configure other parameters, pertaining to single-scanning mode as needed.
configuration.useCase = scanningMode;
// Set an array of accepted barcode types.
// configuration.recognizerConfiguration.barcodeFormats = [
// scanbotV2.BarcodeFormat.AZTEC,
// scanbotV2.BarcodeFormat.PDF_417,
// scanbotV2.BarcodeFormat.QR_CODE,
// scanbotV2.BarcodeFormat.MICRO_QR_CODE,
// scanbotV2.BarcodeFormat.MICRO_PDF_417,
// scanbotV2.BarcodeFormat.ROYAL_MAIL,
/// .....
// ];
// Configure other parameters as needed.
var result = await ScanbotBarcodeSdk.startBarcodeScanner(configuration);
if(result.operationResult == OperationResult.SUCCESS)
{
// TODO: present barcode result as needed
print(result.value?.items.first.type?.name);
}
} catch (e) {
print('Error: $e');
}
}
void _startBarcodeMultiScanning() async {
try {
// Create the default configuration object.
var configuration = BarcodeScannerConfiguration();
// Initialize the use case for multiple scanning.
var scanningMode = MultipleScanningMode();
// Set the counting mode.
scanningMode.mode = MultipleBarcodesScanningMode.COUNTING;
// Set the sheet mode for the barcodes preview.
scanningMode.sheet.mode = SheetMode.COLLAPSED_SHEET;
// Set the height for the collapsed sheet.
scanningMode.sheet.collapsedVisibleHeight = CollapsedVisibleHeight.LARGE;
// Enable manual count change.
scanningMode.sheetContent.manualCountChangeEnabled = true;
// Set the delay before same barcode counting repeat.
scanningMode.countingRepeatDelay = 1000;
// Configure the submit button.
scanningMode.sheetContent.submitButton.text = "Submit";
scanningMode.sheetContent.submitButton.foreground.color =
ScanbotColor("#000000");
// Configure other parameters, pertaining to multiple-scanning mode as needed.
configuration.useCase = scanningMode;
// Set an array of accepted barcode types.
// configuration.recognizerConfiguration.barcodeFormats = [
// BarcodeFormat.AZTEC,
// BarcodeFormat.PDF_417,
// BarcodeFormat.QR_CODE,
// BarcodeFormat.MICRO_QR_CODE,
// BarcodeFormat.MICRO_PDF_417,
// BarcodeFormat.ROYAL_MAIL,
/// .....
// ];
// Configure other parameters as needed.
var result = await ScanbotBarcodeSdk.startBarcodeScanner(configuration);
if(result.operationResult == OperationResult.SUCCESS)
{
// TODO: present barcode result as needed
print(result.value?.items.first.type?.name);
}
} catch (e) {
print('Error: $e');
}
}
void _startAROverlay() async {
try {
// Create the default configuration object.
var configuration = new BarcodeScannerConfiguration();
// Configure the usecase.
var usecase = new MultipleScanningMode();
usecase.mode = MultipleBarcodesScanningMode.UNIQUE;
usecase.sheet.mode = SheetMode.COLLAPSED_SHEET;
usecase.sheet.collapsedVisibleHeight = CollapsedVisibleHeight.SMALL;
// Configure AR Overlay.
usecase.arOverlay.visible = true;
usecase.arOverlay.automaticSelectionEnabled = false;
// Set the configured usecase.
configuration.useCase = usecase;
// Set an array of accepted barcode types.
// configuration.recognizerConfiguration.barcodeFormats = [
// BarcodeFormat.AZTEC,
// BarcodeFormat.PDF_417,
// BarcodeFormat.QR_CODE,
// BarcodeFormat.MICRO_QR_CODE,
// BarcodeFormat.MICRO_PDF_417,
// BarcodeFormat.ROYAL_MAIL,
/// .....
// ];
var result = await ScanbotBarcodeSdk.startBarcodeScanner(configuration);
if (result.operationResult == OperationResult.SUCCESS) {
// TODO: present barcode result as needed
print(result.value?.items.first.type?.name);
}
} catch (e) {
print('Error: $e');
}
}
}
Now let’s build and run the app again so we can try out our new scanning modules.
5. Building the app
We’re all done setting up our app.
Use flutter run
to test the final result.
.NET MAUI
Requirements
There are several options for developing .NET MAUI applications, including the ability to build and launch projects from the .NET CLI if you are so inclined. Whatever method you choose, make sure to download the latest .NET version for your development platform of choice. You’ll also need Microsoft’s build of OpenJDK 17.
In this tutorial, we’re going to develop our app with Android and iOS as target platforms.
⚠️ A note for Windows users: If you want to develop your app for iOS, additional setup is required, including having a Mac available as a build server. You can learn more about this in the .NET MAUI documentation. In this tutorial, we’ll assume you’re developing on a Mac and using the CLI.
If this is your first time developing a .NET MAUI application on your machine, execute the following command:
sudo dotnet workload install maui
1. Preparing the project
First, create a new directory for your project and navigate into it:
mkdir test-maui
cd test-maui
Then create the project with:
dotnet new maui
Since we need access to the device cameras to scan barcodes, let’s add the necessary camera permissions for Android and iOS.
For Android, add the following to Platforms/Android/AndroidManifest.xml inside the <manifest>
element:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
For iOS, add the following to Platforms/iOS/Info.plist anywhere inside the <dict>
element:
<key>NSCameraUsageDescription</key>
<string>Please provide camera access.</string>
2. Installing the SDK
In this tutorial, we’re using SDK version 6.1.0. You can find the latest version in our changelog.
Add the following code to the test-maui.csproj file in your project folder:
<ItemGroup>
<PackageReference Condition="$(TargetFramework.Contains('ios'))" Include="ScanbotBarcodeSDK.MAUI" Version="6.1.0" />
<PackageReference Condition="$(TargetFramework.Contains('android'))" Include="ScanbotBarcodeSDK.MAUI" Version="6.1.0" />
</ItemGroup>
Then run dotnet restore
.
💡 If there are package conflicts, add the appropriate <PackageReference>
tags to the project and make sure <PackageReference>
has NoWarn="NU1605"
added to it to suppress the related build error for that particular package.
Now that the project is set up, we can start integrating the barcode scanning functionalities.
3. Initializing the SDK
In MauiProgram.cs, initialize the Barcode Scanner SDK by replacing the contents with the following code:
using Microsoft.Extensions.Logging;
using ScanbotSDK.MAUI;
// Replace test_maui with the namespace of your app.
namespace test_maui;
public static class MauiProgram
{
// Without a license key, the Scanbot Barcode SDK will work for 1 minute.
// To scan longer, register for a trial license key here: https://scanbot.io/trial/
public const string LicenseKey = "";
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
SBSDKInitializer.Initialize(LicenseKey, new SBSDKConfiguration
{
EnableLogging = true,
ErrorHandler = (status, feature) =>
{
Console.WriteLine($"License error: {status}, {feature}");
}
});
return builder.Build();
}
}
💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key using the <ApplicationId>
found in test-maui.csproj.
For the scanning components to work correctly in Android, you also need to add DependencyManager.RegisterActivity(this)
to the OnCreate
method in Platforms/Android/MainActivity.cs:
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
ScanbotSDK.MAUI.DependencyManager.RegisterActivity(this);
}
💡 If there’s no OnCreate
method in your MainActivity.cs, add the code snippet above to the MainActivity
class.
4. Implementing the scanning modes
Our RTU UI components make it easy to deploy our Barcode Scanner SDK’s different scanning modes in your app. Let’s start with the simplest use case: single-barcode scanning.
In your project folder, go to MainPage.xaml.cs and replace the content with the following code:
using ScanbotSDK.MAUI;
using ScanbotSDK.MAUI.Barcode;
using ScanbotSDK.MAUI.Common;
// Replace test_maui with the namespace of your app.
namespace test_maui;
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage()
{
InitializeComponent();
}
private async void StartSingleScanning(object sender, EventArgs e)
{
try
{
if (!ScanbotSDKMain.LicenseInfo.IsValid)
{
await DisplayAlert(title: "License invalid", message: "Trial license expired.", cancel: "Dismiss");
return;
}
// Create the default configuration object.
var configuration = new BarcodeScannerConfiguration();
// Initialize the single-scan use case.
var singleUsecase = new SingleScanningMode();
// Enable and configure the confirmation sheet.
singleUsecase.ConfirmationSheetEnabled = true;
singleUsecase.SheetColor = Color.FromArgb("#FFFFFF");
// Hide/unhide the barcode image of the confirmation sheet.
singleUsecase.BarcodeImageVisible = true;
// Configure the barcode title of the confirmation sheet.
singleUsecase.BarcodeTitle.Visible = true;
singleUsecase.BarcodeTitle.Color = Color.FromArgb("#000000");
// Configure the barcode subtitle of the confirmation sheet.
singleUsecase.BarcodeSubtitle.Visible = true;
singleUsecase.BarcodeSubtitle.Color = Color.FromArgb("#000000");
// Configure the cancel button of the confirmation sheet.
singleUsecase.CancelButton.Text = "Close";
singleUsecase.CancelButton.Foreground.Color = Color.FromArgb("#C8193C");
singleUsecase.CancelButton.Background.FillColor = Color.FromArgb("#00000000");
// Configure the submit button of the confirmation sheet.
singleUsecase.SubmitButton.Text = "Submit";
singleUsecase.SubmitButton.Foreground.Color = Color.FromArgb("#FFFFFF");
singleUsecase.SubmitButton.Background.FillColor = Color.FromArgb( "#C8193C");
// Set the configured use case.
configuration.UseCase = singleUsecase;
// Create and set an array of accepted barcode formats.
configuration.RecognizerConfiguration.BarcodeFormats = BarcodeFormats.Common;
var result = await ScanbotSDKMain.RTU.BarcodeScanner.LaunchAsync(configuration);
var barcodeAsText = result.Items.Select(barcode => $"{barcode.Type}: {barcode.Text}")
.FirstOrDefault() ?? string.Empty;
await DisplayAlert("Found barcode", barcodeAsText, "Finish");
}
catch (TaskCanceledException)
{
// For when the user cancels the action.
}
catch (Exception ex)
{
// For any other errors that occur.
Console.WriteLine(ex.Message);
}
}
}
Now let’s go to MainPage.xaml and edit the button on the start screen so it calls our StartSingleScanning
method when clicked:
<Button
x:Name="SingleScanningBtn"
Text="Start single-barcode scanning"
SemanticProperties.Hint="Starts the process to scan a single barcode"
Clicked="StartSingleScanning"
HorizontalOptions="Fill" />
If you like, you can build and run the app to see if everything is working correctly so far:
iOS: dotnet build . -f net8.0-ios -t:Run -r ios-arm64
Android: dotnet build . -f net8.0-android -t:Run

In the next step, we’re going to insert both the multi-barcode scanning and the AR overlay modules.
Let’s go back to MainPage.xaml.cs and insert the two methods StartMultiScanning
and StartAROverlay
:
private async void StartMultiScanning(object sender, EventArgs e)
{
try
{
if (!ScanbotSDKMain.LicenseInfo.IsValid)
{
await DisplayAlert(title: "License invalid", message: "Trial license expired.", cancel: "Dismiss");
return;
}
// Create the default configuration object.
var configuration = new BarcodeScannerConfiguration();
// Initialize the multi-scan use case.
var multiUsecase = new MultipleScanningMode();
// Set the counting repeat delay.
multiUsecase.CountingRepeatDelay = 1000;
// Set the counting mode.
multiUsecase.Mode = MultipleBarcodesScanningMode.Counting;
// Set the sheet mode of the barcodes preview.
multiUsecase.Sheet.Mode = SheetMode.CollapsedSheet;
// Set the height of the collapsed sheet.
multiUsecase.Sheet.CollapsedVisibleHeight = CollapsedVisibleHeight.Large;
// Enable manual count change.
multiUsecase.SheetContent.ManualCountChangeEnabled = true;
// Configure the submit button.
multiUsecase.SheetContent.SubmitButton.Text = "Submit";
multiUsecase.SheetContent.SubmitButton.Foreground.Color = Color.FromArgb("#000000");
configuration.UseCase = multiUsecase;
// Create and set an array of accepted barcode formats.
configuration.RecognizerConfiguration.BarcodeFormats = BarcodeFormats.Common;
var result = await ScanbotSDKMain.RTU.BarcodeScanner.LaunchAsync(configuration);
var barcodesAsText = result.Items.Select(barcode => $"{barcode.Type}: {barcode.Text}").ToArray();
await DisplayActionSheet("Found barcodes", "Finish", null, barcodesAsText);
}
catch (TaskCanceledException)
{
// For when the user cancels the action.
}
catch (Exception ex)
{
// For any other errors that occur.
Console.WriteLine(ex.Message);
}
}
private async void StartAROverlay(object sender, EventArgs e)
{
try
{
if (!ScanbotSDKMain.LicenseInfo.IsValid)
{
await DisplayAlert(title: "License invalid", message: "Trial license expired.", cancel: "Dismiss");
return;
}
// Create the default configuration object.
var configuration = new BarcodeScannerConfiguration();
// Configure the usecase.
var usecase = new MultipleScanningMode();
usecase.Mode = MultipleBarcodesScanningMode.Unique;
usecase.Sheet.Mode = SheetMode.CollapsedSheet;
usecase.Sheet.CollapsedVisibleHeight = CollapsedVisibleHeight.Small;
// Configure AR Overlay.
usecase.ArOverlay.Visible = true;
usecase.ArOverlay.AutomaticSelectionEnabled = false;
// Set the configured usecase.
configuration.UseCase = usecase;
// Create and set an array of accepted barcode formats.
configuration.RecognizerConfiguration.BarcodeFormats = BarcodeFormats.Common;
var result = await ScanbotSDKMain.RTU.BarcodeScanner.LaunchAsync(configuration);
var barcodeAsText = result.Items.Select(barcode => $"{barcode.Type}: {barcode.Text}")
.FirstOrDefault() ?? string.Empty;
await DisplayAlert("Found barcode", barcodeAsText, "Finish");
}
catch (TaskCanceledException)
{
// For when the user cancels the action.
}
catch (Exception ex)
{
// For any other errors that occur.
Console.WriteLine(ex.Message);
}
}
And let’s not forget to insert the buttons in our MainPage.xaml:
<Button
x:Name="MultiScanningBtn"
Text="Start multi-barcode scanning"
SemanticProperties.Hint="Starts the process to scan multiple barcodes"
Clicked="StartMultiScanning"
HorizontalOptions="Fill" />
<Button
x:Name="AROverlayBtn"
Text="Starts the AR overlay"
SemanticProperties.Hint="Starts the process to scan barcodes using an AR overlay"
Clicked="StartAROverlay"
HorizontalOptions="Fill" />
Now let’s build and run the app again so we can try out our new scanning modules.
5. Building the app
Now let’s build and run the app again so we can try out our new scanning modules.
iOS: dotnet build . -f net8.0-ios -t:Run -r ios-arm64
Android: dotnet build . -f net8.0-android -t:Run
💡 If you run into errors while building the Android app, try the following command:
dotnet build . -f net8.0-android -t:Run --no-incremental
The option --no-incremental
disables incremental building. When used, it forces a clean rebuild of the entire project, ignoring any previously built components.
Capacitor
Requirements
Before starting development with Capacitor, you need to set up your environment.
The prerequisites for developing Ionic Capacitor apps are:
Core requirements:
- Node.js version 18 or higher
For iOS development:
- macOS
- Xcode
- Xcode Command Line Tools
- Homebrew
- Cocoapods
For Android development:
- Android Studio
- Android SDK
To get the full benefits and experience from using our SDK, we also recommend real devices for testing.
Developing Capacitor applications can be done via CLI or using the VS Code extension. In this tutorial we will use CLI commands. However, feel free to follow along using whatever option suits you best.
Capacitor can be used with different frameworks. In this tutorial, we will use Capacitor with Ionic and Angular. Again, these choices depend on your needs and prior knowledge.
Since we’ll use Ionic in this tutorial, you also need to install the Ionic CLI if you haven’t already:
npm install -g @ionic/cli
1. Preparing the project
To start creating your Capacitor app with Ionic using the CLI, run the following command in the terminal, which will create a blank project with some recommended Capacitor dependencies:
ionic start CapacitorTutorial blank --capacitor --type angular-standalone --package-id io.scanbot.tutorial.capacitor
⚠️ When using your own license, make sure that the package-id
is the same as the application/bundle ID associated with it.
Generate the native projects
Now, let’s add the Android and iOS platforms to our project.
Navigate into the project directory:
cd CapacitorTutorial
Add Android and iOS as platforms:
npm i @capacitor/android @capacitor/ios
Run the following commands to create the native Android and iOS projects:
npx cap add android
npx cap add ios
2. Installing the SDK
To install the Scanbot Capacitor Barcode Scanner SDK, run:
npm i capacitor-plugin-scanbot-barcode-scanner-sdk
💡 This will install the latest Scanbot SDK version. You can find more information about each version in our changelog.
Now that the npm package has been installed, we need to make some changes to the native projects.
Android
For Android, we need to add the camera permission and feature in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
iOS
For iOS, we need to include a description for the camera permission in ios/App/App/Info.plist anywhere inside the <dict>
element:
<key>NSCameraUsageDescription</key>
<string>Camera permission is needed to scan barcodes</string>
Now that the project is set up, we can start integrating the barcode scanning functionalities.
3. Initializing the SDK
Before using any feature of the Capacitor Barcode Scanner SDK, we need to initialize it. Ideally, initialization should be done once, as soon as the app is fully launched.
In this tutorial, we’re going to initialize the SDK inside the ngOnInit
callback in the src/app/app.component.ts file and make sure that we import the ScanbotBarcodeSDK
. Your app.component.ts should then look like this:
import { Component, OnInit } from '@angular/core';
import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
import { ScanbotBarcodeSDK } from 'capacitor-plugin-scanbot-barcode-scanner-sdk';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
standalone: true,
imports: [IonApp, IonRouterOutlet],
})
export class AppComponent implements OnInit {
constructor() { }
ngOnInit(): void {
ScanbotBarcodeSDK.initializeSdk({
licenseKey: ""
}).then(result => console.log(result))
.catch(err => console.log(err));
}
}
💡 Without a license key, our SDK only runs for 60 seconds per session. This is more than enough for the purposes of our tutorial, but if you like, you can generate a license key using the application/bundle ID.
4. Implementing the scanning modes
Our RTU UI components make it easy to deploy our Barcode Scanner SDK’s different scanning modes in your app. Let’s start with the simplest use case: single-barcode scanning.
In your project folder, go to scr/app/home/home.page.ts, add the necessary imports, and create a method that will start the single-barcode scanning use case. The result should look something like this:
import { Component } from '@angular/core';
import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton } from '@ionic/angular/standalone';
import { ScanbotBarcodeSDK } from 'capacitor-plugin-scanbot-barcode-scanner-sdk';
import { startBarcodeScanner, BarcodeScannerConfiguration, SingleScanningMode } from 'capacitor-plugin-scanbot-barcode-scanner-sdk/ui_v2';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
standalone: true,
imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton],
})
export class HomePage {
constructor() {}
async startSingleBarcodeScan() {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for single scanning */
config.useCase = new SingleScanningMode();
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result if there are scanned barcodes */
if (result.data && result.data?.items.length > 0) {
alert(
'Barcode Scanning successfully! \n' +
`Value: ${result.data.items[0].text} \n` +
`Type: ${result.data.items[0].type}`
);
}
} catch (e: any) {
console.error("An error has occurred while running Barcode Scanner", e.message);
}
}
}
Next, go to scr/app/home/home.page.html and add a button that calls our startSingleBarcodeScan
method when clicked. Your home.page.html should look something like this:
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Capacitor Tutorial
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Capacitor Tutorial</ion-title>
</ion-toolbar>
</ion-header>
<div id="container">
<ion-button (click)="startSingleBarcodeScan()">Start single barcode scanning</ion-button>
</div>
</ion-content>
We can add our multi-scanning and AR overlay use cases in a similar manner.
Just add MultipleScanningMode
to the import:
import { startBarcodeScanner, BarcodeScannerConfiguration, SingleScanningMode, MultipleScanningMode } from 'capacitor-plugin-scanbot-barcode-scanner-sdk/ui_v2';
And create two more methods:
async startMultiBarcodeScan() {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for multi-scanning */
config.useCase = new MultipleScanningMode();
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result */
if (result.data && result.data?.items.length > 0) {
alert(
'Barcode Scanning successfully! \n' +
`${result.data.items.map(barcode =>
`Barcode value: ${barcode.text} and type: ${barcode.type}`
).join("\n")}`);
}
} catch (e: any) {
console.error("An error has occurred while running Barcode Scanner", e.message);
}
}
async startAROverlayBarcodeScan() {
try {
/** Check license status and return early if the license is not valid */
if (!(await ScanbotBarcodeSDK.getLicenseInfo()).data?.isLicenseValid) {
return;
}
/**
* Instantiate a configuration object of BarcodeScannerConfiguration and
* start the barcode scanner with the configuration
*/
const config = new BarcodeScannerConfiguration();
/** Initialize the use case for multi-scanning */
config.useCase = new MultipleScanningMode();
/** Configure AR Overlay. */
config.useCase.arOverlay.visible = true;
config.useCase.arOverlay.automaticSelectionEnabled = false;
/** Start the BarcodeScanner */
const result = await startBarcodeScanner(config);
/** Handle the result */
if (result.data && result.data?.items.length > 0) {
alert(
'Barcode Scanning successfully! \n' +
`${result.data.items.map(barcode =>
`Barcode value: ${barcode.text} and type: ${barcode.type}`
).join("\n")}`);
}
} catch (e: any) {
console.error("An error has occurred while running Barcode Scanner", e.message);
}
}
Next, go back to scr/app/home/home.page.html and add buttons for the multi-scanning and AR overlay use cases:
<ion-button (click)="startMultiBarcodeScan()">Start multi-barcode scanning</ion-button>
<ion-button (click)="startAROverlayBarcodeScan()">Start AR Overlay barcode scanning</ion-button>
We’re now ready to build and run the app!
5. Building the app
In the terminal, run these commands to build and sync the native projects:
npm run build
npx cap sync
To run the app on Android and iOS, use the following commands:
For Android:
npx cap run android
For iOS:
To run the app on a real iOS device, you need to adjust the Provisioning and Signing settings. Open ios/App/App.xcworkspace with Xcode, manage the signing and provisioning settings, and run the project from Xcode or with this command:
npx cap run ios
Conclusion
If this tutorial has piqued your interest in integrating barcode scanning functionalities into your cross-platform app, make sure to take a look at our SDK’s other neat features in our documentation or run one of our example projects on GitHub.
Should you have questions about this tutorial or ran into any issues, we’re happy to help! Just shoot us an email via tutorial-support@scanbot.io.
Happy coding!