Skip to content

How to integrate Flutter ZXing into your mobile app

Kevin September 5, 2024 23 mins read
app store

Flutter ZXing is a plugin for scanning and generating barcodes using the ZXing barcode scanning library. The plugin is implemented using the Dart FFI (Foreign Function Interface) and the zxing-cpp library and allows you to integrate barcode scanning and generation functionalities into your Flutter apps.

In this tutorial, we’ll walk you through the process of setting up a Flutter project and implementing an Android barcode scanner using flutter_zxing.

Prerequisites

Before you begin, ensure you have the following tools and components installed:

  1. Android Studio or Visual Studio Code: You need an Integrated Development Environment (IDE) to write and run your Flutter code. Android Studio is recommended for its support for Android development.
  2. Android device or emulator: You will need a physical Android device or an Android emulator set up in your IDE to test your application.

Setting up the Flutter environment

To start, you have to install the Flutter SDK:

  1. Visit the Flutter installation guide.
  2. Select your operating system from the available options.
  3. Choose Android as your development platform.
  4. Follow the instructions under Download and Install to download and unzip the Flutter SDK. When unzipping the file, pay attention to any path restrictions mentioned in the guide.
  5. If you’re a Windows user, after unzipping the SDK, make sure to update your Windows Path Variable as detailed in the guide.

Next, you’ll need to set up your Android development environment:

  1. Visit the Flutter guide for Android setup.
  2. Follow the instructions specific to your situation—whether you’re a new or existing Android Studio user. Choose the guide that best suits you.
  3. Agree to the Flutter Android licenses by following the license agreement instructions.
  4. Finally, verify your development setup to ensure everything is configured correctly.

Creating the app

The following steps cover all the steps to create the Flutter barcode scanner app.

Step 1: Create the project

Before diving into the barcode scanner functionality, let’s start by setting up a basic Flutter project. This will lay the groundwork for all the features we’ll add later on. Follow these steps to get your project up and running:

  1. Open your terminal and access the directory where you want to create the app.
  2. Create a new Flutter project by running the following command:
flutter create barcode_scanner
    cd barcode_scanner
      flutter run
        ZXing Flutter tutorial
        ZXing Flutter tutorial

        Step 2: Install the Flutter ZXing plugin

        With your basic Flutter app up and running, it’s time to start building the barcode scanner functionality. To do this, you’ll use the Flutter ZXing plugin. Follow these steps to install the plugin and configure your app:

        1. Open your project in Android Studio. Once it is open, switch to the Project Files view to locate the necessary folders and files easily.
        ZXing Flutter tutorial
          dependencies:  
            flutter:  
              sdk: flutter  
            cupertino_icons: ^1.0.8  
            flutter_zxing: ^1.7.0
            flutter pub get

            Step 3: Configure the app

            Now that the basics are set up, it’s time to configure the app as a barcode scanner. Start by opening the main.dart file in the lib folder. Delete the default Flutter code to begin building your barcode scanner app from scratch.

            3.1: Import the packages and create the main function

            The first step in configuring your app is to import the required packages and set up the main() function:

            import 'package:flutter/foundation.dart';  
            import 'package:flutter/material.dart';  
            import 'package:flutter_zxing/flutter_zxing.dart';
            
            void main() {  
              runApp(const MyApp());  
            }

            3.2: Create the MyApp class

            Next, you’ll create the MyApp class, which is a stateless widget that serves as the root of your application. This class returns a MaterialApp widget, providing the basic structure and theme for your app.

            1. MaterialApp: A wrapper that includes app title, debugShowCheckedModeBanner, and home settings.
            2. home: Points to the DemoPage widget, which will be the main screen of the app.

            Below is the code to set up the MyApp class:

            class MyApp extends StatelessWidget {  
              const MyApp({super.key});
            
              @override  
              Widget build(BuildContext context) {  
                return const MaterialApp(  
                  title: 'Flutter ZXing Barcode Scanner',  
                  home: DemoPage(),  
                );  
              }  
            }

            3.3. Create the DemoPage widget

            The DemoPage widget is the core of the barcode scanner app, handling its main functionality. In Flutter, when a widget needs to manage a mutable state, you use a StatefulWidget along with its corresponding State class. In this case, the mutable state is necessary to track the scanned barcode, which determines whether to display the barcode scan result.

            The DemoPage setup consists of:

            1. DemoPage StatefulWidget: Manages the state of the app’s main screen.
            2. _DemoPageState: Contains the logic and functions behind the main screen, including tracking the barcode scan result. The result is stored as a nullable Code object.

            The following table lists the methods within the _DemoPageState class:

            MethodDescriptionFunctionality
            build(BuildContext)The primary method for building the widget tree. It checks the platform support and either shows the scanner interface or an unsupported platform message.The build method is called whenever setState is invoked, triggering a rebuild of the widget tree. It constructs the UI based on the current state. If the platform supports the camera, it displays either the ScanResultWidget (if a valid scan result exists) or the scanner interface (by calling _buildScanner). If not, it displays the UnsupportedPlatformWidget. ScanResult and UnsupportedPlatform are external widgets which you’ll create in the following tutorial sections.
            _isCameraSupported()Helper method to check if the current platform supports the camera.Returns a boolean value indicating whether the current platform supports camera functionality. It specifically checks for iOS and Android platforms, which are supported by the flutter_zxing library.
            _buildScanner()Helper method that returns the ReaderWidget, which is used to scan barcodes.Constructs the scanner interface using the ReaderWidget. It configures the scanner with parameters such as resolution, lens direction, and scan format. It also provides callback methods (onScan, onScanFailure, onControllerCreated) to handle scan results and errors. The ReaderWidget is provided by the flutter_zxing library. For a list of all configurations available, access the GitHub repository.
            _onControllerCreated(_, Exception? error)Callback method triggered when the scanner controller is created. Handles any errors that occur during the controller’s creation.Called when the ReaderWidget‘s controller is created. If an error occurs during creation, such as permission issues, it calls _showMessage to display an error message to the user.
            _onScanSuccess(Code? code)Callback method that is called when a barcode is successfully scanned. It updates the result state and triggers a UI rebuild.Triggered when a barcode is successfully scanned. It updates the result variable with the scanned code data and calls setState to rebuild the widget tree. The UI is then updated to display the scan result using ScanResultWidget.
            _onScanFailure(Code? code)Callback method that handles scan failures. It updates the result state and displays an error message if available.Triggered when a scan attempt fails. It updates the result variable with the failed code data and calls setState to rebuild the widget tree. If the failed code contains an error message, it is displayed to the user via _showMessage.
            _showMessage(BuildContext, String message)Helper method that displays a SnackBar with a provided message.Displays a message to the user related to errors or important information. It first hides any currently visible SnackBar and then shows a new SnackBar with the specified message. This ensures that the user is notified of any issues or updates during scanning.

            The following code block presents the code for DemoPage and _DemoPageState:

            class MyApp extends StatelessWidget {  
             const MyApp({super.key});
            
             @override  
             Widget build(BuildContext context) {  
               return const MaterialApp(  
                 title: 'Flutter ZXing Barcode Scanner',  
                 debugShowCheckedModeBanner: false,  
                 home: DemoPage(),  
               );  
             }  
            }
            
            class DemoPage extends StatefulWidget {  
             const DemoPage({super.key});
            
             @override  
             State<DemoPage> createState() => _DemoPageState();  
            }
            
            class _DemoPageState extends State<DemoPage> {  
             Code? result;
            
             @override  
             Widget build(BuildContext context) {  
               if (kIsWeb || !_isCameraSupported()) {  
                 return const UnsupportedPlatformWidget();  
               }
            
               return Scaffold(  
                 appBar: AppBar(title: const Text('Scan Code')),  
                 body: result != null && result?.isValid == true  
                     ? ScanResultWidget(  
                   result: result,  
                   onScanAgain: () => setState(() => result = null),  
                 )  
                     : _buildScanner(),  
               );  
             }
            
             bool _isCameraSupported() {  
               return defaultTargetPlatform == TargetPlatform.iOS ||  
                   defaultTargetPlatform == TargetPlatform.android;  
             }
            
             Widget _buildScanner() {  
               return ReaderWidget(  
                 onScan: _onScanSuccess,  
                 onScanFailure: _onScanFailure,  
                 onControllerCreated: _onControllerCreated,  
                 resolution: ResolutionPreset.high,  
                 lensDirection: CameraLensDirection.back,  
                 codeFormat: Format.*any*,  
                 showGallery: false,  
                 cropPercent: 0.7,  
                 toggleCameraIcon: const Icon(Icons.*switch\_camera*),  
                 actionButtonsBackgroundBorderRadius: BorderRadius.circular(10),  
               );  
             }
            
             void _onControllerCreated(_, Exception? error) {  
               if (error != null) {  
                 _showMessage(context, 'Error: $error');  
               }  
             }
            
             void _onScanSuccess(Code? code) {  
               setState(() {  
                 result = code;  
               });  
             }
            
             void _onScanFailure(Code? code) {  
               setState(() {  
                 result = code;  
               });  
               if (code?.error?.isNotEmpty == true) {  
                 _showMessage(context, 'Error: ${code?.error}');  
               }  
             }
            
             void _showMessage(BuildContext context, String message) {  
               ScaffoldMessenger.*of*(context).hideCurrentSnackBar();  
               ScaffoldMessenger.*of*(context).showSnackBar(  
                 SnackBar(content: Text(message)),  
               );  
             }  
            }

            For this tutorial, the following configurations are used to define the ReaderWidget behavior:

            ConfigurationDescription
            resolution: ResolutionPreset.highSets the camera resolution to high.
            lensDirection: CameraLensDirection.backConfigures the camera to use the back camera.
            codeFormat: Format.anyAllows the scanner to detect any barcode format.
            showGallery: falseDisables the option to select images from the gallery for scanning. It only allows scanning barcodes directly using the camera.
            cropPercent: 0.7Defines the percentage of the camera preview used for scanning. Increasing this value will increase the app screen area used to identify barcodes.
            toggleCameraIcon: const Icon(Icons.switch_camera)Sets the icon used for toggling between front and back cameras
            actionButtonsBackgroundBorderRadius: BorderRadius.circular(10)Applies a border radius of 10 pixels to the background of action buttons

            Access the GitHub repository for a list of all available ReaderWidget configurations.

            3.4 Create the ScanResult widget

            The _DemoPageState build method relies on the ScanResultWidget to display the scan result in the app. This widget also includes a Scan Again button to allow users to restart the scanning process.

            To create the ScanResultWidget, start by creating a new folder named widget inside the lib directory. Inside this folder, add a new file named scan_result_widget.dart. This file will define how the scan result is presented to users. It’s important to note that since the ScanResultWidget is stateless, it doesn’t manage any internal state. Everything it displays is passed in through its properties.

            After creating the file, define the ScanResultWidget class:

            class ScanResultWidget extends StatelessWidget {  
              const ScanResultWidget({  
                super.key,  
                this.result,  
                this.onScanAgain,  
              });
            
              final Code? result;  
              final Function()? onScanAgain;
            }

            In the above code, the result is a final property of type Code? holding the scan result, and onScanAgain is a final property of type Function()?, which is a callback function triggered when the user presses the Scan Again button.

            Next, add the components that will make up the UI. Below is the complete code for the ScanResultWidget, including its basic UI configuration:

            import 'package:flutter/material.dart';  
            import 'package:flutter_zxing/flutter_zxing.dart';
            
            class ScanResultWidget extends StatelessWidget {  
              const ScanResultWidget({  
                super.key,  
                this.result,  
                this.onScanAgain,  
              });
            
              final Code? result;  
              final Function()? onScanAgain;
            
              @override  
              Widget build(BuildContext context) {  
                return Center(  
                  child: Padding(  
                    padding: const EdgeInsets.all(20.0),  
                    child: Column(  
                      mainAxisAlignment: MainAxisAlignment.center,  
                      children: [  
                        Text(  
                          result?.format?.name ?? '',  
                          style: Theme.of(context).textTheme.headlineSmall,  
                        ),  
                        const SizedBox(height: 20),  
                        Text(  
                          result?.text ?? '',  
                          style: Theme.of(context).textTheme.titleLarge,  
                        ),  
                        const SizedBox(height: 20),  
                        Text(  
                          'Inverted: ${result?.isInverted}  Mirrored: ${result?.isMirrored}',  
                          style: Theme.of(context).textTheme.bodyMedium,  
                        ),  
                        const SizedBox(height: 40),  
                        ElevatedButton(  
                          onPressed: onScanAgain,  
                          child: const Text('Scan Again'),  
                        ),  
                      ],  
                    ),  
                  ),  
                );  
              }  
            }

            There are two main elements in the above code:

            1. Text: Display the barcode format, the scanned text, and additional information.
            2. ElevatedButton: This button allows users to scan again by triggering the onScanAgain callback.

            3.5 Create the Unsupported Platform widget

            The _DemoPageState build method uses the UnsupportedPlatformWidget to inform the user if the current device and platform do not support camera usage. To create the UnsupportedPlatformWidget, create a new file named unsupported_platform_widget.dart.dart inside the widgets folder.

            Here’s what your lib folder structure will look like after adding the file:

            /lib  
            |--- main.dart  
            |--- /widgets  
            |------ scan\_result\_widget.dart  
            |------ unsupported\_platform\_widget.dat

            To build the UnsupportedPlatformWidget, use the following code. This widget will display a message to users indicating that their platform isn’t supported, with the message centered on the screen and styled according to the app’s theme.

            import 'package:flutter/material.dart';
            
            class UnsupportedPlatformWidget extends StatelessWidget {  
             const UnsupportedPlatformWidget({super.key});
            
             @override  
             Widget build(BuildContext context) {  
               return Center(  
                 child: Text(  
                   'This platform is not supported yet.',  
                   style: Theme.of(context).textTheme.titleLarge,  
                 ),  
               );  
             }  
            }

            Step 4: Testing the app

            With the main.dart file updated and the scan_result_widget.dart and unsupported_platform_widget.dart files created, you’re ready to test your barcode scanner app. Follow these steps to get started:

            1. Connect your Android device to your computer or configure an emulator.
            2. Select your device or emulator in Android Studio.
            3. In the Run/Debug selector, choose the main.dart file.
            ZXing Flutter tutorial

              The GIF below demonstrates how the barcode scanner app works. Since the ReaderWidget is configured with Format.any, the app successfully identifies both Code 128 and EAN-13 barcode formats. The scanning area is set to 70% of the screen width, which restricts barcode detection to this area. You can adjust this setting through the ReaderWidget configurations. In addition, the GIF shows that the app will not provide a result if more than one barcode is within the scanning area. It only identifies a barcode when a single barcode is visible.

              If you change Format.any to Format.code128, the app will exclusively identify Code 128 barcodes. The following GIF demonstrates this behavior. Notice how the app no longer reads EAN-13 barcodes and is quicker at detecting Code 128 barcodes.

              🎉 And that’s it! You’ve successfully added barcode scanning functionalities to your Flutter app.

              Disadvantages of using the ZXing barcode scanner library

              ZXing provides decent performance for basic barcode scanning tasks but sometimes struggles with more challenging scenarios. Its most notable drawbacks as a barcode scanning solution include:

              • Scanning accuracy and speed: ZXing struggles with scanning poorly lit or damaged barcodes. It often exhibits low recognition rates for smaller barcodes and can fail to decode them altogether.
              • Compatibility issues: ZXing may not perform reliably across all devices, particularly newer models. This has led to frustration among developers who require consistent performance across different platforms.
              • Lack of active development: As an open-source project, ZXing has seen limited updates and maintenance in recent years. This stagnation can lead to unresolved bugs and compatibility issues, making it less reliable for commercial applications.
              • Integration complexity: Integrating ZXing into your app can be cumbersome, especially for developers who may not be familiar with its architecture. This can lead to longer development times and increased chances of bugs during implementation.

              For companies that heavily rely on barcode scanning in their business processes, we recommend using an enterprise-grade solution instead.

              Building a Flutter Barcode Scanner with the Scanbot SDK

              We developed the Scanbot Barcode Scanner SDK to help enterprises overcome the hurdles presented by free barcode scanning software. Our goal was to have a developer-friendly solution available for a wide range of platforms that consistently delivers high-quality results – even in challenging circumstances.

              With the SDK’s Ready-to-Use UI Components, you can even use an AR overlay to display multiple barcodes’ contents directly in the viewfinder, enhancing the scanning experience.

              Setting up a Flutter app to scan 1D or 2D barcodes is straightforward. We’re going to follow these steps:

              1. Prepare the project.
              2. Install the SDK.
              3. Initialize the SDK.
              4. Implement the scanning modules.
              5. Start scanning barcodes!

              Requirements

              To develop Flutter applications for Android and iOS, ensure you have the following:

              1. Flutter SDK: Download the latest version for your OS.
              2. Dart SDK: No separate installation needed, since it’s integrated with Flutter.
              3. The right IDEs for your target platforms:
                1. Android Studio to develop for Android. It includes the Android SDK and AVD Manager.
                2. Xcode for iOS development on macOS.
                3. 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 commnand 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. Scanning some barcodes!

              Now you can go ahead and scan 1D and 2D barcodes – one after the other, many at the same time, and even with an AR overlay that lets you preview their values!

              And if you’re in need of some sample barcodes for testing purposes, we’ve got you covered:

              Various barcodes for testing

              If this tutorial has piqued your interest in integrating barcode scanning functionalities into your Flutter app, make sure to take a look at our SDK’s other neat features in our documentation.

              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!