blog-teaser

How to replace Flutter Cognex with barKoder into your mobile app

The objective of this tutorial is to walk you through the process of setting up a Flutter project and implementing an Android barcode scanner using cmbSDK_Flutter and barkoder_flutter.

Flutter Development Setup

Essential Tools

  • IDE: Android Studio or Visual Studio Code
    • Android Studio is recommended for its robust Android development support
  • Testing Environment: Android device or emulator
    • Physical Android device or
    • Android emulator configured in your IDE

Flutter SDK Installation

  1. Visit the Flutter installation guide
  2. Select your operating system
  3. Choose Android as the development platform
  4. Download and unzip the Flutter SDK
  5. For Windows users: Update the Windows Path Variable

Android Development Environment Setup

  1. Follow the Flutter guide for Android setup
  2. Set up based on your experience level (new or existing Android Studio user)
  3. Accept Flutter Android licenses
  4. Verify your development setup

Verification Process
Run the following command in your terminal or command prompt:

flutter doctor

This command will check your environment and display a report of the status of your Flutter installation. Address any issues it identifies to ensure a smooth development experience.

Additional Considerations

IDE Configuration

  • Install the Flutter and Dart plugins in your chosen IDE
  • Configure the Flutter SDK path in your IDE settings

Emulator Setup (Optional)
If using an emulator:

  1. Open Android Studio
  2. Navigate to Tools > AVD Manager
  3. Create and configure a virtual device

Version Control
Consider setting up version control for your project:

  • Initialize a Git repository
  • Create a .gitignore file to exclude unnecessary files

By following these steps, you'll have a robust Flutter development environment ready for building Android applications.

This is a guide for the barKoder Flutter Plugin. Follow the simple steps below to use the barkoder_flutter package in your Flutter project:

1. Prepare the environment  

Install Flutter SDK and setup your environment

2. Add your new barkoder flutter package 

In your pubspec.yaml file, you will find the Scanbot Flutter Barcode Scanner package added. You must replace that package with barkoder_flutterIn your pubspec.yaml file, you will find the Cognex Flutter Barcode Scanner package added. Please replace that package with barkoder_flutter
dependencies:
  cmbsdk_flutter: ^1.2.276

After you remove the Cognex plugin please  add the barkoder_flutter package to your project, 

dependencies:
    flutter:
      sdk: flutter
   barkoder_flutter: <package version>

If you prefer to use a local package, download the package from the barKoder Portal and set the package path in your project's dependencies:

dependencies:
      flutter:
        sdk: flutter
        barkoder_flutter:
        path: <package local path>

3. Install dependencies 

Run flutter pub get command in terminal to install the specified dependencies in your project

flutter pub get

4. Import package 

Import the barkoder_flutter package in your project with after you remove the Cognex one.

import 'package:cmbsdk_flutter/cmbsdk_flutter.dart';
barkoder:
import 'package:barkoder_flutter/barkoder_flutter.dart';

5. Initializing the SDK and BarkoderView 

Look at the Cognex ;  main.dart
void _createReaderDevice() {
    if (_deviceType == cmbDeviceType.Camera) {
      cmb.setCameraMode(_cameraMode);
      cmb.setPreviewOptions(cmbPrevewiOption.Defaults);
      cmb.registerSDK("SDK_KEY");
    } else if (_deviceType == cmbDeviceType.Bluetooth) {
      if (Platform.isIOS) {
        final String? pairedPeripheralUUID =
            prefs.getString(pairedPeripheralUUIDKey);
        if (pairedPeripheralUUID != null) {
          cmb.setPairedBluetoothDevice(pairedPeripheralUUID);
        } else {
          cmb.setPairedBluetoothDevice(""); // clear it
        }
      } else
        cmb.setPairedBluetoothDevice("BT_Device_MAC_Address");
    }
    cmb.loadScanner(_deviceType).then((value) {
      cmb.connect().then((value) {
        _updateUIByConnectionState(cmbConnectionState.Connected);
      }).catchError((error, stackTrace) {
        _updateUIByConnectionState(cmbConnectionState.Disconnected);
      });
    }).catchError((error, stackTrace) => print('${error.message}'));
  }

At this point the barkoder_flutter package is installed and imported in your project. Next step is to add the BarkoderView in your layout and set the licenseKey parameter and onBarkoderViewCreated callback.

There is a key in Cognex cmb.registerSDK("SDK_KEY"); that needs to be replaced with the current barKoder license key in the licenseKey: 'KEY',

@override
  Widget build(BuildContext context) {
    return Scaffold(
      ...,
      body: BarkoderView(
                licenseKey: 'KEY',
                onBarkoderViewCreated: _onBarkoderViewCreated),
      ...
    );

The license key is a string that consists of alphanumerical characters. See section Section 8 to learn how to get a valid license.

6. Ready to Scan Event 

This ScanEvent Cognex looks exactly like the one below for scanning some barcodes, but you will need to replace the code below with the code provided by us.

Cognex Scan Event void _configureReaderDevice() {  

void _toggleScanner() {
    if (_isScanning) {
      cmb
          .stopScanning()
          .catchError((error, stackTrace) => print('${error.message}'));
    } else {
    
      cmb
          .startScanning()
          .catchError((error, stackTrace) => print('${error.message}'));
          
    }
  }
void _configureReaderDevice() {
    //----------------------------------------------
    // Explicitly enable the symbologies we need
    //----------------------------------------------
    cmb
        .setSymbologyEnabled(cmbSymbology.DataMatrix, true)
        .then((value) => print('DataMatrix enabled'))
        .catchError((error, stackTrace) =>
            print('DataMatrix NOT enabled. ${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.C128, true)
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.UpcEan, true)
        .catchError((error, stackTrace) => print('${error.message}'));
    //-------------------------------------------------------
    // Explicitly disable symbologies we know we don't need
    //-------------------------------------------------------
    cmb
        .setSymbologyEnabled(cmbSymbology.CodaBar, false)
        .then((value) => print('CodaBar disabled'))
        .catchError((error, stackTrace) =>
            print('CodaBar NOT disabled. ${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.C93, false)
        .catchError((error, stackTrace) => print('${error.message}'));
    //---------------------------------------------------------------------------
    // Below are examples of sending DMCC commands and getting the response
    //---------------------------------------------------------------------------
    cmb
        .sendCommand('GET DEVICE.TYPE')
        .then((value) => print('$value'))
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .sendCommand('GET DEVICE.FIRMWARE-VER')
        .then((value) => print('$value'))
        .catchError((error, stackTrace) => print('${error.message}'));
    //---------------------------------------------------------------------------
    // We are going to explicitly turn off image results (although this is the
    // default). The reason is that enabling image results with an MX-1xxx
    // scanner is not recommended unless your application needs the scanned
    // image--otherwise scanning performance can be impacted.
    //---------------------------------------------------------------------------
    cmb
        .enableImage(false)
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .enableImageGraphics(false)
        .catchError((error, stackTrace) => print('${error.message}'));
    //---------------------------------------------------------------------------
    // Device specific configuration examples
    //---------------------------------------------------------------------------
    if (_deviceType == cmbDeviceType.Camera) {
      //---------------------------------------------------------------------------
      // Phone/tablet
      //---------------------------------------------------------------------------
      // Set the SDK's decoding effort to level 3
      cmb
          .sendCommand("SET DECODER.EFFORT 3")
          .catchError((error, stackTrace) => print('${error.message}'));
    } else if (_deviceType == cmbDeviceType.MXReader) {
      //---------------------------------------------------------------------------
      // MX-1xxx
      //---------------------------------------------------------------------------
      //---------------------------------------------------------------------------
      // Save our configuration to non-volatile memory
      // If the MX hibernates or is rebooted, our settings will be retained.
      //---------------------------------------------------------------------------
      cmb
          .sendCommand("CONFIG.SAVE")
          .catchError((error, stackTrace) => print('${error.message}'));
    } else if (_deviceType == cmbDeviceType.Bluetooth) {
      //---------------------------------------------------------------------------
      // DM-8700
      //---------------------------------------------------------------------------
      //---------------------------------------------------------------------------
      // Save our configuration to non-volatile memory
      // If the Reader hibernates or is rebooted, our settings will be retained.
      //---------------------------------------------------------------------------
      cmb
          .sendCommand("CONFIG.SAVE")
          .catchError((error, stackTrace) => print('${error.message}'));
    }
  }

Inside _onBarkoderViewCreated callback function the SDK is fully initialized and ready for configuration or to start the scanning process

void _onBarkoderViewCreated(barkoder) {
  _barkoder = barkoder;
  _barkoder.setBarcodeTypeEnabled(BarcodeType.qr, true);
  _barkoder.setRegionOfInterestVisible(true);
  ...
}
void _scanPressed() {
  _barkoder.startScanning((result) {
    // The result from successful scan is received here
  });
}

For the complete usage of the barkoder_flutter plugin please check our sample.

7. Camera permissions 

Our SDK requires camera permission to be granted in order to use scanning features. For Android, the permission is set in the manifest from the plugin. For iOS you need to specify camera permission in Info.plist file inside your project

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>Camera permission</string>

8. Licensing 

The SDK will scan barcodes even without a valid license; however all results will be randomly masked with (*) Asterisk characters. By using our software you are agreeing to our End User License Agreement. To obtain a valid license, one should create an account here and either get a trial license (to test the software out) or procure a production license that can be used within a live app.

9. Example example/lib/main.dart

Cogenx main.dart  looks something like this:

import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:cmbsdk_flutter/cmbsdk_flutter.dart';
import 'package:cmbsdk_flutter/cmbsdk_flutter_constants.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
  runApp(
    new MaterialApp(
      home: new MyApp(),
    ),
  );
}
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  //----------------------------------------------------------------------------
  // The cmbSDK supports multi-code scanning (scanning multiple barcodes at
  // one time); thus scan results are returned as an array. Note that
  // this sample app does not demonstrate the use of these multi-code features.
  //----------------------------------------------------------------------------
  List<dynamic> _resultsArray = [];
  bool _isScanning = false;
  String _cmbSDKVersion = 'N/A';
  String _connectionStatusText = 'Disconnected';
  Color _connectionStatusBackground = Colors.redAccent;
  String _scanButtonText = '(NOT CONNECTED)';
  bool _scanButtonEnabled = false;
  late SharedPreferences prefs;
  final String pairedPeripheralUUIDKey = 'pairedPeripheralUUID';
  BuildContext? deviceDialogContext;
  //----------------------------------------------------------------------------
  // If USE_PRECONFIGURED_DEVICE is YES, then the app will create a reader
  // using the values of device/cameraMode. Otherwise, the app presents
  // a pick list for the user to select either MX-1xxx or the built-in camera.
  //----------------------------------------------------------------------------
  final bool _usePreconfiguredDevice = false;
  int _deviceType = cmbDeviceType.MXReader;
  int _cameraMode = cmbCameraMode.NoAimer;
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addObserver(this);
    initCmbSDK();
  }
  @override
  void dispose() {
    WidgetsBinding.instance!.removeObserver(this);
    super.dispose();
  }
  @override
  void didChangeAppLifecycleState(AppLifecycleState appLifecycleState) {
    switch (appLifecycleState) {
      case AppLifecycleState.resumed:
        // dismiss the device selector dialog
        if (deviceDialogContext != null) {
          Navigator.pop(deviceDialogContext!);
        }
        cmb
            .connect()
            .catchError((error, stackTrace) => print('${error.message}'));
        break;
      case AppLifecycleState.paused:
        cmb
            .disconnect()
            .catchError((error, stackTrace) => print('${error.message}'));
        break;
    }
  }
  Future<void> savePairedPeripheral() async {
    final String? pairedperipheralUUID = await cmb
        .getPairedBluetoothDevice()
        .catchError((error, stackTrace) => print(error.message));
    if (pairedperipheralUUID != null)
      prefs.setString(pairedPeripheralUUIDKey, pairedperipheralUUID);
  }
  Future<void> initCmbSDK() async {
    String cmbSDKVersion = 'N/A';
    prefs = await SharedPreferences.getInstance();
    // This is called when a MX-1xxx device has became available (USB cable was plugged, or MX device was turned on),
    // or when a MX-1xxx that was previously available has become unavailable (USB cable was unplugged, turned off due to inactivity or battery drained)
    cmb.setAvailabilityChangedListener((availability) {
      if (availability == cmbAvailability.Available.index) {
        cmb
            .connect()
            .catchError((error, stackTrace) => print('${error.message}'));
      } else {
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              content: Text('Device became unavailable'),
              actions: [
                TextButton(
                  child: Text('OK'),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
              ],
            );
          },
        );
      }
    });
    // This is called when a connection with the reader has been changed.
    // The reader is usable only in the "Connected" state
    cmb.setConnectionStateChangedListener((state) {
      if (state == cmbConnectionState.Connected.index) {
        if (Platform.isIOS && _deviceType == cmbDeviceType.Bluetooth) {
          savePairedPeripheral();
        }
        _configureReaderDevice();
        _updateUIByConnectionState(cmbConnectionState.Connected);
      } else {
        _updateUIByConnectionState(cmbConnectionState.Disconnected);
      }
      if (!mounted) return;
      setState(() {
        _isScanning = false;
        _resultsArray = [];
      });
    });
    // This is called after scanning has completed, either by detecting a barcode,
    // canceling the scan by using the on-screen button or a hardware trigger button, or if the scanning timed-out
    cmb.setReadResultReceivedListener((resultJSON) {
      final Map<String, dynamic> resultMap = jsonDecode(resultJSON);
      final List<dynamic> resultsArray = resultMap['results'];
      if (!mounted) return;
      setState(() {
        _resultsArray = resultsArray;
      });
    });
    // It will return TRUE in the result if the scanning process is STARTED and false if it's STOPPED
    cmb.setScanningStateChangedListener((scanningState) {
      if (!mounted) return;
      setState(() {
        _isScanning = scanningState;
        _scanButtonText = _isScanning ? 'STOP SCANNING' : 'START SCANNING';
      });
    });
    // Get cmbSDK version number
    cmbSDKVersion = await cmb.sdkVersion;
    //initialize and connect to MX/Phone Camera here
    if (_usePreconfiguredDevice) {
      _createReaderDevice();
    } else {
      _selectDeviceFromPicker();
    }
    if (!mounted) return;
    setState(() {
      _cmbSDKVersion = cmbSDKVersion;
    });
  }
  // Update the UI of the app (scan button, connection state label) depending on the current connection state
  void _updateUIByConnectionState(cmbConnectionState state) {
    String connectionStatusText = _connectionStatusText;
    Color connectionStatusBackground = _connectionStatusBackground;
    String scanButtonText = _scanButtonText;
    bool scanButtonEnabled = _scanButtonEnabled;
    if (state == cmbConnectionState.Connected) {
      connectionStatusText = 'Connected';
      connectionStatusBackground = Colors.lightGreen;
      scanButtonText = 'START SCANNING';
      scanButtonEnabled = true;
    } else {
      connectionStatusText = 'Disconnected';
      connectionStatusBackground = Colors.redAccent;
      scanButtonText = '(NOT CONNECTED)';
      scanButtonEnabled = false;
    }
    if (!mounted) return;
    setState(() {
      _connectionStatusText = connectionStatusText;
      _connectionStatusBackground = connectionStatusBackground;
      _scanButtonText = scanButtonText;
      _scanButtonEnabled = scanButtonEnabled;
    });
  }
  //----------------------------------------------------------------------------
  // This is the pick list for choosing the type of reader connection
  //----------------------------------------------------------------------------
  Future<void> _selectDeviceFromPicker() async {
    int deviceType = _deviceType;
    int cameraMode = _cameraMode;
    List<Widget> deviceOptions = <Widget>[
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, cmbDeviceType.Bluetooth);
        },
        child: const Text('Paired Bluetooth Scanner'),
      ),
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, cmbDeviceType.MXReader);
        },
        child: const Text('MX Scanner (MX-1xxx)'),
      ),
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, cmbDeviceType.Camera);
        },
        child: const Text('Phone Camera'),
      ),
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, null);
        },
        child: const Text('Cancel'),
      ),
    ];
    if (Platform.isIOS) {
      deviceOptions.insert(
          0,
          SimpleDialogOption(
            onPressed: () {
              prefs.remove(pairedPeripheralUUIDKey);
              Navigator.pop(context, cmbDeviceType.Bluetooth);
            },
            child: const Text('New Bluetooth Scanner'),
          ));
    }
    bool dismissed = false;
    switch (await showDialog<int>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          deviceDialogContext = context;
          return SimpleDialog(
            title: const Text('Select device'),
            children: deviceOptions,
          );
        })) {
      case cmbDeviceType.Bluetooth:
        deviceType = cmbDeviceType.Bluetooth;
        break;
      case cmbDeviceType.MXReader:
        deviceType = cmbDeviceType.MXReader;
        break;
      case cmbDeviceType.Camera:
        deviceType = cmbDeviceType.Camera;
        cameraMode = cmbCameraMode.NoAimer;
        // ...
        break;
      default:
        // dialog dismissed
        dismissed = true;
        break;
    }
    deviceDialogContext = null;
    if (!mounted) return;
    setState(() {
      _deviceType = deviceType;
      _cameraMode = cameraMode;
    });
    if (dismissed == false) {
      _createReaderDevice();
    }
  }
  // Create a reader using the selected option from "selectDeviceFromPicker"
  void _createReaderDevice() {
    if (_deviceType == cmbDeviceType.Camera) {
      cmb.setCameraMode(_cameraMode);
      cmb.setPreviewOptions(cmbPrevewiOption.Defaults);
      cmb.registerSDK("SDK_KEY");
    } else if (_deviceType == cmbDeviceType.Bluetooth) {
      if (Platform.isIOS) {
        final String? pairedPeripheralUUID =
            prefs.getString(pairedPeripheralUUIDKey);
        if (pairedPeripheralUUID != null) {
          cmb.setPairedBluetoothDevice(pairedPeripheralUUID);
        } else {
          cmb.setPairedBluetoothDevice(""); // clear it
        }
      } else
        cmb.setPairedBluetoothDevice("BT_Device_MAC_Address");
    }
    cmb.loadScanner(_deviceType).then((value) {
      cmb.connect().then((value) {
        _updateUIByConnectionState(cmbConnectionState.Connected);
      }).catchError((error, stackTrace) {
        _updateUIByConnectionState(cmbConnectionState.Disconnected);
      });
    }).catchError((error, stackTrace) => print('${error.message}'));
  }
  //----------------------------------------------------------------------------
  // This is an example of configuring the device. In this sample application, we
  // configure the device every time the connection state changes to connected (see
  // the ConnectionStateChanged event), as this is the best
  // way to garentee it is setup the way we want it. Not only does this garentee
  // that the device is configured when we initially connect, but also covers the
  // case where an MX scanner has hibernated (and we're reconnecting)--unless
  // setting changes are explicitly saved to non-volatile memory, they can be lost
  // when the MX hibernates or reboots.
  //
  // These are just example settings; in your own application you will want to
  // consider which setting changes are optimal for your application. It is
  // important to note that the different supported devices have different, out
  // of the box defaults:
  //
  //    * MX-1xxx Mobile Terminals have the following symbologies enabled by default:
  //        - Data Matrix
  //        - UPC/EAN
  //        - Code 39
  //        - Code 93
  //        - Code 128
  //        - Interleaved 2 of 5
  //        - Codabar
  //    * camera scanner has NO symbologies enabled by default
  //
  // For the best scanning performance, it is recommended to only have the barcode
  // symbologies enabled that your application actually needs to scan. If scanning
  // with an MX-1xxx, that may mean disabling some of the defaults (or enabling
  // symbologies that are off by default).
  //
  // Keep in mind that this sample application works with all three types of devices,
  // so in our example below we show explicitly enabling symbologies as well as
  // explicitly disabling symbologies (even if those symbologies may already be on/off
  // for the device being used).
  //
  // We also show how to send configuration commands that may be device type
  // specific--again, primarily for demonstration purposes.
  //----------------------------------------------------------------------------
  void _configureReaderDevice() {
    //----------------------------------------------
    // Explicitly enable the symbologies we need
    //----------------------------------------------
    cmb
        .setSymbologyEnabled(cmbSymbology.DataMatrix, true)
        .then((value) => print('DataMatrix enabled'))
        .catchError((error, stackTrace) =>
            print('DataMatrix NOT enabled. ${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.C128, true)
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.UpcEan, true)
        .catchError((error, stackTrace) => print('${error.message}'));
    //-------------------------------------------------------
    // Explicitly disable symbologies we know we don't need
    //-------------------------------------------------------
    cmb
        .setSymbologyEnabled(cmbSymbology.CodaBar, false)
        .then((value) => print('CodaBar disabled'))
        .catchError((error, stackTrace) =>
            print('CodaBar NOT disabled. ${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.C93, false)
        .catchError((error, stackTrace) => print('${error.message}'));
    //---------------------------------------------------------------------------
    // Below are examples of sending DMCC commands and getting the response
    //---------------------------------------------------------------------------
    cmb
        .sendCommand('GET DEVICE.TYPE')
        .then((value) => print('$value'))
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .sendCommand('GET DEVICE.FIRMWARE-VER')
        .then((value) => print('$value'))
        .catchError((error, stackTrace) => print('${error.message}'));
    //---------------------------------------------------------------------------
    // We are going to explicitly turn off image results (although this is the
    // default). The reason is that enabling image results with an MX-1xxx
    // scanner is not recommended unless your application needs the scanned
    // image--otherwise scanning performance can be impacted.
    //---------------------------------------------------------------------------
    cmb
        .enableImage(false)
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .enableImageGraphics(false)
        .catchError((error, stackTrace) => print('${error.message}'));
    //---------------------------------------------------------------------------
    // Device specific configuration examples
    //---------------------------------------------------------------------------
    if (_deviceType == cmbDeviceType.Camera) {
      //---------------------------------------------------------------------------
      // Phone/tablet
      //---------------------------------------------------------------------------
      // Set the SDK's decoding effort to level 3
      cmb
          .sendCommand("SET DECODER.EFFORT 3")
          .catchError((error, stackTrace) => print('${error.message}'));
    } else if (_deviceType == cmbDeviceType.MXReader) {
      //---------------------------------------------------------------------------
      // MX-1xxx
      //---------------------------------------------------------------------------
      //---------------------------------------------------------------------------
      // Save our configuration to non-volatile memory
      // If the MX hibernates or is rebooted, our settings will be retained.
      //---------------------------------------------------------------------------
      cmb
          .sendCommand("CONFIG.SAVE")
          .catchError((error, stackTrace) => print('${error.message}'));
    } else if (_deviceType == cmbDeviceType.Bluetooth) {
      //---------------------------------------------------------------------------
      // DM-8700
      //---------------------------------------------------------------------------
      //---------------------------------------------------------------------------
      // Save our configuration to non-volatile memory
      // If the Reader hibernates or is rebooted, our settings will be retained.
      //---------------------------------------------------------------------------
      cmb
          .sendCommand("CONFIG.SAVE")
          .catchError((error, stackTrace) => print('${error.message}'));
    }
  }
  void _toggleScanner() {
    if (_isScanning) {
      cmb
          .stopScanning()
          .catchError((error, stackTrace) => print('${error.message}'));
    } else {
    
      cmb
          .startScanning()
          .catchError((error, stackTrace) => print('${error.message}'));
          
    }
  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: Color(0xff333333),
      appBar: AppBar(
        title: const Text('CMBCameraDemo'),
        actions: <Widget>[
          Padding(
              padding: const EdgeInsets.all(20.0),
              child: GestureDetector(
                onTap: () {
                  _selectDeviceFromPicker();
                },
                child: const Text('DEVICE'),
              )),
        ],
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Expanded(
                child: ListView.separated(
                    padding: const EdgeInsets.all(10),
                    itemCount: _resultsArray.length,
                    itemBuilder: (BuildContext context, int index) {
                      return Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: <Widget>[
                            Text('${_resultsArray[index]['readString']}',
                                style: TextStyle(
                                    color: Colors.white, fontSize: 18)),
                            Text('${_resultsArray[index]['symbologyString']}',
                                style: TextStyle(color: Colors.grey)),
                          ]);
                    },
                    separatorBuilder: (BuildContext context, int index) =>
                        const Divider(thickness: 1))),
            Padding(
                padding: const EdgeInsets.all(10),
                child: Container(
                    width: double.infinity,
                    height: 50,
                    child: ElevatedButton(
                      onPressed: _scanButtonEnabled
                          ? () {
                              _toggleScanner();
                            }
                          : null,
                      child: Text(_scanButtonText),
                      style: ElevatedButton.styleFrom(
                          backgroundColor: Color(0xfffadb04),
                          foregroundColor: Colors.black,
                          disabledBackgroundColor: Color(0xfffadb04)),
                    ))),
            Padding(
                padding: const EdgeInsets.all(5),
                child: Row(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Text(_cmbSDKVersion,
                        style: TextStyle(
                          color: Colors.white,
                        )),
                    Container(
                      color: _connectionStatusBackground,
                      child: Padding(
                          padding: EdgeInsets.all(2),
                          child: Text(_connectionStatusText,
                              style: TextStyle(
                                color: Colors.white,
                              ))),
                    )
                  ],
                ))
          ],
        ),
      ),
    );
  }
}

Your new barKoder Flutter final main.dart should look something like this:

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:barkoder_flutter/barkoder_flutter.dart';
import 'package:image_picker/image_picker.dart';
void main() {
  runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  late Barkoder _barkoder;
  bool _isScanningActive = false;
  String _barkoderVersion = '';
  List<DecoderResult> _decoderResults = [];
  List<Uint8List?> _resultThumbnailImages = [];
  Uint8List? _resultImage;
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }
  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    if (state == AppLifecycleState.paused) {
      // _barkoder.stopScanning();
      // _updateState(null, false);
    }
  }
  void _onBarkoderViewCreated(barkoder) async {
    _barkoder = barkoder;
    String barkoderVersion = await _barkoder.getVersion;
    _setActiveBarcodeTypes();
    _setBarkoderSettings();
    // _barkoder.configureBarkoder(BarkoderConfig(
    //   imageResultEnabled: true,
    //   decoder: DekoderConfig(qr: BarcodeConfig(enabled: true)),
    // ));
    if (!mounted) return;
    setState(() {
      _barkoderVersion = barkoderVersion;
    });
  }
  void _setActiveBarcodeTypes() {
    _barkoder.setBarcodeTypeEnabled(BarcodeType.qr, true);
    _barkoder.setBarcodeTypeEnabled(BarcodeType.ean13, true);
    _barkoder.setBarcodeTypeEnabled(BarcodeType.upcA, true);
    _barkoder.setBarcodeTypeEnabled(BarcodeType.code128, true);
  }
  void _setBarkoderSettings() {
    _barkoder.setImageResultEnabled(true);
    _barkoder.setLocationInImageResultEnabled(true);
    _barkoder.setMaximumResultsCount(200);
    _barkoder.setRegionOfInterestVisible(true);
    _barkoder.setPinchToZoomEnabled(true);
    _barkoder.setBarcodeThumbnailOnResultEnabled(true);
    _barkoder.setRegionOfInterest(5, 5, 90, 90);
    // When using image scan it is recommended to use rigorous decoding speed
    // _barkoder.setDecodingSpeed(DecodingSpeed.rigorous);
  }
  void _updateState(BarkoderResult? barkoderResult, bool scanningIsActive) {
    if (!mounted) return;
    setState(() {
      _isScanningActive = scanningIsActive;
      if (barkoderResult != null) {
        _decoderResults.clear();
        _resultThumbnailImages.clear();
        _resultImage?.clear();
        _decoderResults.addAll(barkoderResult.decoderResults);
        if (barkoderResult.resultThumbnails != null) {
          _resultThumbnailImages.addAll(barkoderResult.resultThumbnails!);
        }
        if (barkoderResult.resultImage != null) {
          _resultImage = barkoderResult.resultImage!;
        }
      } else {
        // Clear all values if no result
        _decoderResults.clear();
        _resultThumbnailImages.clear();
        _resultImage = null;
      }
    });
  }
  void _scanPressed() {
    if (_isScanningActive) {
      _barkoder.stopScanning();
    } else {
      _barkoder.startScanning((result) {
        _updateState(result, false);
      });
    }
    _updateState(null, !_isScanningActive);
  }
  void _scanImage() async {
    final ImagePicker _picker = ImagePicker();
    final XFile? pickedFile =
        await _picker.pickImage(source: ImageSource.gallery);
    _barkoder.stopScanning();
    if (pickedFile != null) {
      final bytes = await pickedFile.readAsBytes();
      String base64Image = base64Encode(bytes);
      _barkoder.scanImage((result) {
        if (result.decoderResults.isEmpty) {
          // Only show dialog if no results were found
          _showNoResultsDialog();
        }
        _updateState(result, false);
      }, base64Image);
    } else {
      _updateState(null, true);
    }
    _updateState(null, !_isScanningActive);
  }
  void _showNoResultsDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("No barcodes or MRZ detected :("),
          content: const Text(
              "Please ensure the image you've selected contains at least one barcode, or choose a different image.nAlso, verify that the barcode type you're trying to scan is enabled"),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: const Text("OK"),
            ),
          ],
        );
      },
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F5F5),
      appBar: AppBar(
        backgroundColor: const Color(0xFFFF0022),
        title: Text('Barkoder Sample (v$_barkoderVersion)'),
      ),
      body: Stack(
        children: [
          Column(
            children: [
              Expanded(
                child: Stack(
                  children: <Widget>[
                    // Camera preview
                    BarkoderView(
                      licenseKey: '',
                      onBarkoderViewCreated: _onBarkoderViewCreated,
                    ),
                    // Placeholder Text
                    if (!_isScanningActive && _resultImage == null)
                      const Align(
                        alignment: Alignment.center,
                        child: Text(
                          'Press the button to start scanning',
                          textAlign: TextAlign.center,
                          style: TextStyle(color: Colors.grey, fontSize: 20),
                        ),
                      ),
                    // Display scanned image on top of preview
                    if (_resultImage != null)
                      Align(
                        alignment: Alignment.center,
                        child: Image.memory(
                          _resultImage!,
                          fit: BoxFit.contain,
                          width: MediaQuery.of(context).size.width,
                          height: MediaQuery.of(context).size.height,
                        ),
                      ),
                  ],
                ),
              ),
              Expanded(
                child: ListView.builder(
                  itemCount: _decoderResults.isNotEmpty
                      ? _decoderResults.length + 1
                      : 0,
                  itemBuilder: (context, index) {
                    if (index == 0) {
                      return Card(
                        margin: const EdgeInsets.all(10.0),
                        color: Colors.white,
                        child: Padding(
                          padding: const EdgeInsets.all(10.0),
                          child: Center(
                            child: Text(
                              '${_decoderResults.length} result${_decoderResults.length > 1 ? 's' : ''} found',
                              style: const TextStyle(
                                fontSize: 16,
                                fontWeight: FontWeight.bold,
                                color: Colors.black,
                              ),
                            ),
                          ),
                        ),
                      );
                    }
                    final result = _decoderResults[index - 1];
                    final thumbnailImage = _resultThumbnailImages.isNotEmpty &&
                            index - 1 < _resultThumbnailImages.length
                        ? _resultThumbnailImages[index - 1]
                        : null;
                    return Card(
                      margin: const EdgeInsets.all(10.0),
                      color: Colors.white,
                      child: Padding(
                        padding: const EdgeInsets.all(10.0),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            const Text(
                              "Result",
                              style: TextStyle(
                                  color: Colors.red,
                                  fontWeight: FontWeight.bold),
                            ),
                            Text(result.textualData),
                            const Divider(),
                            const Text(
                              "Type",
                              style: TextStyle(
                                  color: Colors.red,
                                  fontWeight: FontWeight.bold),
                            ),
                            Text(result.barcodeTypeName),
                            if (result.extra != null &&
                                result.extra!.isNotEmpty) ...[
                              const Divider(),
                              const Text(
                                "Extras",
                                style: TextStyle(
                                    color: Colors.red,
                                    fontWeight: FontWeight.bold),
                              ),
                              Text(result.extra?.toString() ?? ''),
                            ],
                            if (thumbnailImage != null) ...[
                              const Divider(),
                              const Text(
                                "Thumbnail",
                                style: TextStyle(
                                    color: Colors.red,
                                    fontWeight: FontWeight.bold),
                              ),
                              Image.memory(
                                thumbnailImage,
                                fit: BoxFit.contain,
                                width: 100,
                                height: 100,
                              ),
                            ],
                            if (result.mrzImages != null &&
                                result.mrzImages!.isNotEmpty) ...[
                              const Divider(),
                              const Text(
                                "MRZ Images",
                                style: TextStyle(
                                    color: Colors.red,
                                    fontWeight: FontWeight.bold),
                              ),
                              Column(
                                children: result.mrzImages!.map((mrzImage) {
                                  return Padding(
                                    padding: const EdgeInsets.only(top: 8.0),
                                    child: Column(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: [
                                        Text(
                                          mrzImage.name,
                                          style: const TextStyle(
                                              fontWeight: FontWeight.bold,
                                              color: Colors.black),
                                        ),
                                        Image.memory(
                                          mrzImage.value,
                                          fit: BoxFit.contain,
                                          width: 200,
                                          height: 200,
                                        ),
                                      ],
                                    ),
                                  );
                                }).toList(),
                              ),
                            ],
                          ],
                        ),
                      ),
                    );
                  },
                ),
              ),
            ],
          ),
        ],
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.min,
        children: [
          FloatingActionButton(
            onPressed: _scanImage,
            child: const Icon(Icons.photo),
            backgroundColor: Colors.white,
          ),
          const SizedBox(width: 16),
          FloatingActionButton(
            tooltip: _isScanningActive ? 'Stop scan' : 'Start scan',
            onPressed: _scanPressed,
            backgroundColor: _isScanningActive ? Colors.red : Colors.white,
            child: const Icon(Icons.qr_code_scanner),
          ),
        ],
      ),
    );
  }
}

 

 

 

 

 

Latest Barcode Scanner SDK Articles,
Tutorials, and News