Getting Started with Flutter CaptureSDK¶
SDK Installation¶
Adding to pubspec.yaml
dependencies:
flutter:
sdk: flutter
capturesdk_flutter: ^2.0.10
Then run flutter pub get.
Requirements for both iOS and Android platforms¶
Your application will need a SocketMobile AppKey. Follow the link to create an AppKey. AppKeys can be generated online and at no additional cost beyond the nominal registration fee. The AppKey is validated by the SDK library on the device, no internet connection is required. Note: You don’t need to create your own AppKey to compile and run the sample apps.
The scanner needs to be paired with your devices in Application Mode. This can be done using Socket Mobile Companion app (recommended), which can be downloaded from the App Store .
Try our Flutter sample app: Flutter CaptureSDK.
Requirements for iOS¶
The Socket Mobile CaptureSDK uses Bluetooth Classic through the External Accessory framework for the barcode scanner products and Bluetooth Low Energy (BLE) for the Contactless Reader/Writer products (Such as the Socket Mobile D600 and S550).
For applications that need to work with barcode scanners, make sure the following requirements are met:
Your application needs to be registered in our whitelist before submitting your application to the Apple Store. It will not pass the Apple Store review if this is not done.
Your application’s
Info.plistMUST have the keyLSApplicationQueriesSchemes(Queried URL Schemes) with a new item:sktcompanion(in lower case).Your application’s
Info.plistMUST have the keyNSCameraUsageDescriptionwith a string value explaining to the user how the app uses the camera for camera scanning purpose.- Your application’s
Info.plistMUST have the keyUISupportedExternalAccessoryProtocols(Supported External Protocol) with a new item:com.socketmobile.chs(in lower case). 
- Your application’s
Your application’s
Info.plistMUST have the keyNSBluetoothAlwaysUsageDescriptionwith a string value explaining to the user how the app uses this data. This is an iOS/Apple requirement for applications that use the CoreBluetooth framework. Without it, your application will crash with a message in the Xcode console explaining that you must add the description key. Additionally, if your application has a deployment target earlier than iOS 13.0, you will need to addNSBluetoothPeripheralUsageDescriptionalong with theNSBluetoothAlwaysUsageDescription. More information can be found here in the official Apple DocumentationYour application’s
Info.plistMUST have the keyCFBundleAllowMixedLocalizations(Localized resources can be mixed) to `YES`.
Requirements for Android¶
Adding to MainActivity.java
In order for the CaptureSDK plugin to be registered, it needs to be added to the MainActivity.java file. This is done by adding the following code to the MainActivity.java file:
package com.example.example; // Replace with your app's package name
import com.capturesdk_flutter.CaptureModule; // import CaptureModule Native Modules
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
// Add CaptureModule plugin instance from CaptureSDK
flutterEngine.getPlugins().add(new CaptureModule(getApplicationContext())); // this adds the CaptureModule plugin to the FlutterEngine
}
}
Adding to <your-app/app/build.gradle>
In order for your app to download all the necessary dependencies you need to add the following options in your app’s build.gradle file:
buildTypes {
release {
minifyEnabled false <------- to add
shrinkResources false <------- to add
signingConfig = signingConfigs.debug
}
}
Using CaptureSDK¶
First, once the Flutter CaptureSDK package has been installed, it can be imported as shown below:
import 'package:capturesdk_flutter/capturesdk.dart';
Using CaptureHelper (recommended)¶
CaptureHelper is the recommended way to integrate the CaptureSDK into your Flutter app. It manages device handles, event routing, and Bluetooth LE lifecycle automatically, so you don’t have to write the boilerplate code yourself.
Here are the steps to follow:
Create a
CaptureHelperinstanceOpen it with your App credentials and event callbacks
Handle connected devices through the callbacks
Close the helper when done
Step 1: Define your App credentials
Generate your AppInfo from the developer portal. You need separate keys for iOS and Android:
const AppInfo appInfo = AppInfo(
appIdAndroid: 'android:com.example.myapp',
appKeyAndroid: 'MC4CFQ...your_android_key...',
appIdIos: 'ios:com.example.myapp',
appKeyIos: 'MC0CFA...your_ios_key...',
developerId: 'your-developer-id',
);
Step 2: Open CaptureHelper
Create a CaptureHelper instance and call open with your credentials and the callbacks you need. On Android, the Capture service must be started before opening the helper:
final CaptureHelper helper = CaptureHelper();
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
CapturePlugin.startCaptureService().then((_) => _openHelper());
} else {
_openHelper();
}
}
Future<void> _openHelper() async {
try {
await helper.open(
appInfo,
onDeviceArrival: (CaptureHelperDevice device) {
// A scanner connected — update your UI
},
onDeviceRemoval: (CaptureHelperDevice device) {
// A scanner disconnected
},
onDecodedData: (DecodedData data, CaptureHelperDevice device) {
// Barcode scanned
// data.name is the symbology, data.data is the payload
},
onError: (CaptureException e) {
// SDK error
},
);
} on CaptureException catch (e) {
debugPrint('Open failed: ${e.code} ${e.message}');
}
}
@override
void dispose() {
helper.close();
super.dispose();
}
Step 3: Access connected devices
Once a device connects and fires onDeviceArrival, it is available in the helper’s device list. Each CaptureHelperDevice provides typed methods to interact with the device:
List<CaptureHelperDevice> devices = helper.getDevices();
for (final device in devices) {
final name = await device.getFriendlyName();
final battery = await device.getBatteryLevel();
}
Available CaptureHelperDevice methods
getFriendlyName()/setFriendlyName(name)— get or set the device display namegetBatteryLevel()— get the battery level (0–100)getFirmwareVersion()— get the firmware versiongetPowerState()— get the power statesetTrigger(action)— trigger a scan (seeTriggerconstants)getDataSource(id)/setDataSource(id, status:)— query or change the state of a symbologygetDecodeAction()/setDecodeAction(action)— configure the local decode action
Available CaptureHelper callbacks
All callbacks are optional. Register only the ones you need:
onDeviceArrival— a device connected and is readyonDeviceRemoval— a device disconnectedonDecodedData— a barcode was scannedonError— an SDK error occurredonBatteryLevel— battery level changedonPowerState— power state changedonDiscoveredDevice— a Bluetooth LE device was found during discovery (see Using Bluetooth Low Energy Devices with Flutter Capture)onDiscoveryEnd— Bluetooth LE discovery scan finished (see Using Bluetooth Low Energy Devices with Flutter Capture)
Using the low-level API¶
For advanced use cases, you can use the low-level Capture class directly. This gives you full control over device handles and event routing, but requires more boilerplate code.
Here are the usual steps to follow:
Open Capture with the App credentials and provide event handler function
Handle device arrival and open the device in the event handler function
Handle device removal and close the device in the event handler function
Handle decoded data in the event handler function
Opening Capture with App credentials
The Flutter SDK is an extension of the CaptureSDK
Capture capture = Capture(logger);
setState(() {
_capture = capture;
});
final AppInfo appInfo = AppInfo(
'android:com.example.example',
'MC4CFQDNCtjazxILEh8oyT6w/wlaVKqS1gIVAKTz2W6TB9EgmjS1buy0A+3j7nX4',
'ios:com.example.example',
'MC0CFA1nzK67TLNmSw/QKFUIiedulUUcAhUAzT6EOvRwiZT+h4qyjEZo9oc0ONM=',
'bb57d8e1-f911-47ba-b510-693be162686a');
try {
int? response = await capture.openClient(appInfo, _onCaptureEvent);
print('capture open successful.');
} on CaptureException catch (exception) {
print('capture open failed: ${exception.code}');
}
Handle device arrival and open the device
When the application receives a Device Arrival notification, it can create a new Capture instance that represents the new device.
The application opens the device by passing GUID and the main Capture reference as arguments of the device open function.
Opening the device allows to receive the decoded data from this device.
Note
the device GUID changes everytime the device connects. It identifies a connection session with a device.
Note
If a Socket Mobile device is already connected to the host prior to the app opening CaptureSDK, the device arrival notificaiton will still be sent to make the application aware that the device is connected.
Note
The second argument of the _onCaptureEvent, handle, is optional. It could be used to identify which Capture object is the source of the notification.
In the _onCaptureEvent callback passed when opening Capture you could have code similar to this handling the device arrival notification:
_onCaptureEvent(e, handle) {
if (e == null) {
return;
} else if (e.runtimeType == CaptureException) {
_updateVals("${e.code}", e.message, e.method, e.details);
return;
}
logger.log('onCaptureEvent from: ', '$handle');
switch (e.id) {
case CaptureEventIds.deviceArrival:
Capture deviceCapture = Capture(logger);
setState(() {
_deviceCapture = deviceCapture;
});
_openDeviceHelper(deviceCapture, e);
break;
case CaptureEventIds.deviceRemoval:
_closeDeviceHelper(e, handle);
break;
case CaptureEventIds.decodedData:
setState(() {
//storing scanned data in state for future use
_currentScan = e;
});
_updateVals('Decoded Data', "Successful scan!");
break;
}
}
Handle device removal and close the device
The device removal occurs when the Socket Mobile device is no longer connected to the host. It is recommended to close it.
In the _onCaptureEvent callback passed when opening Capture you could have code that executes a helper (seen above) to close the device:
Future<void> _closeDeviceHelper(e, handle) async {
String guid = e.value.guid;
String name = e.value.name;
logger.log('Device Removal =>', name + ' ($guid)');
try {
dynamic res = await _deviceCapture!.close();
if (res == 0) {
List<DeviceInfo> arr = _devices;
arr.removeWhere((element) => element.guid == guid);
setState(() {
_devices = arr;
_currentScan = null;
_deviceCapture = null;
});
}
_updateVals('Device Closed', 'Successfully removed "$name"');
} on CaptureException catch (exception) {
_updateVals('${exception.code}', 'Unable to remove "$name"',
exception.method, exception.details);
}
}
Handle decoded data in the event handler function
Each time a Socket Mobile device is successful at reading a barcode or an NFC tag, the decoded data notification is sent and can be handled as shown here:
Note
Capture does not interpret the decoded data, only the application knows how to interpret it. For demonstration purpose the decoded data can be displayed with the help of a function like this:
// **********************************
// Decoded Data
// receive the decoded data from
// a specific device
// e = {
// id: CaptureEventIds.DecodedData,
// type: CaptureEventTypes.DecodedData,
// value: {
// data: [55, 97, 100, 57, 53, 100, 97, 98, 48, 102, 102, 99, 52, 53, 57, 48, 97, 52, 57, 54, 49, 97, 51, 49, 57, 50, 99, 49, 102, 51, 53, 55],
// id: CaptureDataSourceID.SymbologyQRCode,
// name: "QR Code"
// }
// }
// **********************************
case CaptureEventIds.decodedData:
setState(() {
//storing scanned data in state for future use
_currentScan = e;
});
_updateVals('Decoded Data', "Successful scan!");
break;
// In the Widget build(...)
Text(_currentScan != null
? 'Scan from ${_currentScan!.value.name}: ' +
_currentScan!.value.data.toString()
: 'No Data'))