
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
- Visit the Flutter installation guide
- Select your operating system
- Choose Android as the development platform
- Download and unzip the Flutter SDK
- For Windows users: Update the Windows Path Variable
Android Development Environment Setup
- Follow the Flutter guide for Android setup
- Set up based on your experience level (new or existing Android Studio user)
- Accept Flutter Android licenses
- 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:
- Open Android Studio
- Navigate to Tools > AVD Manager
- 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
pubspec.yaml
file, you will find the Cognex Flutter Barcode Scanner package added. Please replace that package with barkoder_flutterdependencies:
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';
import 'package:barkoder_flutter/barkoder_flutter.dart';
5. Initializing the SDK and BarkoderView
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),
),
],
),
);
}
}
