fluttervisionsdkplugin 0.8.0 fluttervisionsdkplugin: ^0.8.0 copied to clipboard
A Flutter plugin package for integrating the Flutter Vision SDK into your Flutter application for scanning Barcodes and QrCodes
// ignore_for_file: avoid_print, unused_element, library_private_types_in_public_api
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttervisionsdkplugin/settings/camera_settings.dart';
import 'package:fluttervisionsdkplugin_example/secrets.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:fluttervisionsdkplugin/settings/focus_settings.dart';
import 'package:fluttervisionsdkplugin/settings/object_detection_settings.dart';
import 'package:fluttervisionsdkplugin/visioncamerawidget.dart';
void main() {
runApp(const MyApp());
}
int captureTime = 0;
int processingTime = 0;
int totalTime = 0;
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
FlutterToPluginCommunicator? communicator;
final env = Environment.sandbox;
bool isAutoScanning = false;
int scanMode = 1;
int captureMode = 1;
bool shouldDisplayFocusImage = false;
ModelClass selectedModel = ModelClass.shippingLabel;
bool getOnlinePredictions = true;
bool isBarcodeBeingDetected = false;
bool isQRCodeBeingDetected = false;
bool isTextBeingDetected = false;
bool isDocumentBeingDetected = false;
void updateScannedType(bool isText, bool isBarcode, bool isQrCode, bool isDocument) {
setState(() {
isTextBeingDetected = isText;
isBarcodeBeingDetected = isBarcode;
isQRCodeBeingDetected = isQrCode;
isDocumentBeingDetected = isDocument;
});
}
double? _configureProgress;
void updateConfigureProgress(double? configureProgress) {
setState(() {
_configureProgress = configureProgress;
});
}
List<String> _receivedCodes = [];
void updateReceivedCodes(List<String> receivedCodes) {
setState(() {
_receivedCodes = receivedCodes;
});
}
Map<String, dynamic> _ocrResult = {};
void updateOCRResult(Map<String, dynamic> result) {
setState(() {
_ocrResult = result;
});
}
bool isConfigured = false;
void updateIsConfigured(bool isConfigured) {
setState(() {
this.isConfigured = isConfigured;
});
}
@override
void dispose() {
super.dispose();
communicator?.release();
}
@override
Widget build(BuildContext buildContext) {
return MaterialApp(
home: Scaffold(
body: FutureBuilder<PermissionStatus>(
future: Permission.camera.status,
builder: (context, snapshot) {
if (Platform.isIOS || snapshot.data?.isGranted == true) {
return Stack(
children: [
SizedBox(
width: double.infinity,
height: double.infinity,
child: VisionCameraWidget(
environment: env,
onViewCreated: (communicator) {
this.communicator = communicator;
if (isAutoScanning) {
this.communicator?.setScanAuto();
} else {
this.communicator?.setScanManual();
}
this.communicator?.setObjectDetectionSettings(
ObjectDetectionSettings());
this.communicator?.setCameraSettings(CameraSettings());
this.communicator?.setFocusSettings(FocusSettings());
this.communicator?.setMultipleScanEnabled(true);
this.communicator?.startCamera();
},
listener: MainNativeViewWidgetListener(this),
),
),
Align(
alignment: Alignment.topRight,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 100,),
Text('Barcode', style: TextStyle(color: isBarcodeBeingDetected ? Colors.green : Colors.white,)),
Text('QRCode', style: TextStyle(color: isQRCodeBeingDetected ? Colors.green : Colors.white,)),
Text('Text', style: TextStyle(color: isTextBeingDetected ? Colors.green : Colors.white,)),
Text('Document', style: TextStyle(color: isDocumentBeingDetected ? Colors.green : Colors.white,)),
],
),
),
Align(
alignment: Alignment.topCenter,
child: Container(
width: double.infinity,
color: const Color(0x88FFFFFF),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 30,
),
DropdownMenu(
dropDownOptions: const ['Barcode', 'QRCode', 'OCR'],
onItemSelected: (selectedItemIndex) {
switch (selectedItemIndex) {
case 0:
setState(() {
captureMode = 1; // BARCODE
});
communicator?.setCaptureModeBarcode();
break;
case 1:
setState(() {
captureMode = 2; // QRCODE
});
communicator?.setCaptureModeQrCode();
break;
case 2:
setState(() {
captureMode = 3; // OCR
});
communicator?.setCaptureModeOCR();
break;
}
},
),
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: const Color(0x88FFFFFF),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (captureMode == 3 &&
selectedModel !=
ModelClass
.billOfLading) // WHEN CAPTURE MODE IS OCR
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: RadioListTile(
value: true,
title: const Text('Online OCR'),
groupValue: getOnlinePredictions,
onChanged: (i) {
setState(() {
getOnlinePredictions = true;
});
}),
),
Expanded(
child: RadioListTile(
value: false,
title: const Text('On-Device OCR'),
groupValue: getOnlinePredictions,
onChanged: (i) {
setState(() {
getOnlinePredictions = false;
});
}),
),
],
),
if (captureMode == 3) // WHEN CAPTURE MODE IS OCR
DropdownMenu(
dropDownOptions: const [
'Shipping Label',
'Bill Of Lading',
],
onItemSelected: (selectedItemIndex) {
updateIsConfigured(false);
communicator?.release(); // THIS IS IMPORTANT. DO NOT CHANGE MODEL WITHOUT RELEASING PREVIOUS MODEL FIRST.
switch (selectedItemIndex) {
case 0:
selectedModel = ModelClass.shippingLabel;
break;
case 1:
selectedModel = ModelClass.billOfLading;
setState(() {
getOnlinePredictions = true;
});
break;
case 2:
selectedModel = ModelClass.itemLabel;
break;
case 3:
selectedModel =
ModelClass.documentClassification;
break;
}
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: RadioListTile(
value: true,
title: const Text('Auto'),
groupValue: isAutoScanning,
onChanged: (i) {
setState(() {
isAutoScanning = true;
});
communicator?.setScanAuto();
}),
),
Expanded(
child: RadioListTile(
value: false,
title: const Text('Manual'),
groupValue: isAutoScanning,
onChanged: (i) {
setState(() {
isAutoScanning = false;
});
communicator?.setScanManual();
}),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (!isAutoScanning && _configureProgress == null)
ElevatedButton(
onPressed: () {
communicator?.capturePhoto();
},
child: const Text('Capture Photo'),
),
if (_configureProgress != null)
Row(
children: [
SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator()),
const SizedBox(width: 12,),
Text(_configureProgress.toString())
],
),
if (!getOnlinePredictions &&
_configureProgress == null &&
!isConfigured)
ElevatedButton(
onPressed: () async {
updateConfigureProgress(0.0);
communicator?.stopCamera();
await communicator?.configureOnDeviceOCR(
apiKey: getApiKey(env),
modelClass: selectedModel,
modelSize: ModelSize.micro);
},
child: const Text('Configure'),
),
],
),
const SizedBox(
height: 10,
),
],
),
),
),
if (_receivedCodes.isNotEmpty)
AlertDialog(
content: SingleChildScrollView(
child: ListBody(
children: _receivedCodes.map((e) => Text(e)).toList(),
),
),
actions: [
TextButton(
onPressed: () {
communicator?.rescan();
setState(() {
_receivedCodes.clear();
});
},
child: Text('OK'))
],
),
if (_ocrResult.isNotEmpty)
AlertDialog(
content: SingleChildScrollView(
child: Text(
_ocrResult.toString().replaceAll('\n', '')
),
),
actions: [
TextButton(
onPressed: () {
communicator?.rescan();
setState(() {
_ocrResult.clear();
});
},
child: Text('OK'))
],
),
],
);
} else {
return Center(
child: FutureBuilder<bool>(
future: Permission.camera.isPermanentlyDenied,
builder: (context, snapshot) {
if (snapshot.data == true) {
return const Text(
"Go to Settings and allow camera permission");
} else {
return ElevatedButton(
onPressed: () async {
await Permission.camera.request();
setState(() {});
},
child: const Text('Request Camera Permission'));
}
},
));
}
},
),
),
);
}
}
class MainNativeViewWidgetListener implements PluginToFlutterCommunicator {
MainNativeViewWidgetListener(this._state);
final _MyAppState _state;
Timer? _timerTask;
Uint8List? byteArrayImage;
List<String>? codes;
@override
void onCameraStarted() {}
@override
void onCameraStopped() {}
@override
void onCodesReceived(List<String> codes) {
_state.updateReceivedCodes(codes);
}
@override
void onDetectionResult(bool isText, bool isBarcode, bool isQrCode, bool isDocument) {
_state.updateScannedType(isText, isBarcode, isQrCode, isDocument);
}
@override
void onImageCaptured(Uint8List byteArrayImage, List<String> codes) {
captureTime = DateTime.now().millisecondsSinceEpoch - captureTime;
processingTime = DateTime.now().millisecondsSinceEpoch;
this.byteArrayImage = byteArrayImage;
this.codes = codes;
switch (_state.selectedModel) {
case ModelClass.shippingLabel:
if (_state.getOnlinePredictions) {
_state.communicator?.callOcrApi(
apiKey: getApiKey(_state.env),
image: byteArrayImage,
barcodes: codes,
sender: {
'contact_id': 'ctct_hNxjevb74gV62i5XTQvqg6'
},
recipient: {
'contact_id': 'ctct_hNxjevb74gV62i5XTQvqg6'
},
options: {
'match': {
'search': ['recipients'],
'location': true
},
'postprocess': {'require_unique_hash': false},
'transform': {
'tracker': 'outbound',
'use_existing_tracking_number': false
}
},
metadata: {
'Test': 'Pass'
});
} else {
_state.communicator?.getPredictions(byteArrayImage, codes);
}
break;
case ModelClass.billOfLading:
_state.communicator?.callBolApi(
apiKey: getBolApiKey(_state.env),
image: byteArrayImage,
barcodes: codes);
break;
case ModelClass.itemLabel:
if (_state.getOnlinePredictions) {
} else {}
break;
case ModelClass.documentClassification:
if (_state.getOnlinePredictions) {
} else {}
break;
}
}
@override
void onOnDeviceConfigureProgress(double progress) {
print('Configure progress $progress');
_state.updateConfigureProgress(progress);
}
@override
void onOnDeviceConfigurationComplete() {
print('On-Device Configuration Completed');
_state.updateConfigureProgress(null);
_state.communicator?.startCamera();
_state.updateIsConfigured(true);
}
@override
void onOnlineSLResult(Map<String, dynamic> result) {
_state.updateOCRResult(result);
}
@override
void onMatchingResult(Map<String, dynamic> result) {
_state.updateOCRResult(result);
}
@override
void onOnlineBOLResult(Map<String, dynamic> result) {
_state.updateOCRResult(result);
}
@override
void onOnDeviceOCRResult(Map<String, dynamic> result) {
print('ON-DEVICE OCR RESPONSE');
print(result);
byteArrayImage = null;
codes = null;
_state.updateOCRResult(result);
// final processingTimeTaken = DateTime.now().millisecondsSinceEpoch - processingTime;
// final totalTimeTaken = DateTime.now().millisecondsSinceEpoch - totalTime;
// _state.updateScannedCode(
// "CAPTURE TIME: $captureTime millis\nPROCESSING TIME: $processingTimeTaken millis\nTOTAL TIME: $totalTimeTaken millis"
// );
}
@override
void onReportResult(Map<ReportResult, String> reportResult) {
switch (reportResult.keys.first) {
case ReportResult.successful:
// _state.updateResult("Report submitted successfully");
break;
case ReportResult.savedForLater:
// _state.updateResult("Report saved for later");
break;
case ReportResult.failed:
// _state.updateResult(reportResult.values.first);
break;
}
_resetTextAfterDelay();
}
@override
void onScanError(String error) {
// _state.updateResult(error);
_resetTextAfterDelay();
}
void _resetTextAfterDelay() {
_timerTask?.cancel();
_timerTask = Timer(const Duration(seconds: 4), () {
// _state.updateResult('');
_state.communicator?.rescan();
});
}
}
String getApiKey(Environment env) {
return switch (env) {
Environment.qa => throw UnimplementedError(),
Environment.dev => DEV.apiKey,
Environment.staging => STAGING.apiKey,
Environment.sandbox => SANDBOX.apiKey,
Environment.production => throw UnimplementedError(),
};
}
String getBolApiKey(Environment env) {
return switch (env) {
Environment.qa => throw UnimplementedError(),
Environment.dev => DEV.bolApiKey,
Environment.staging => STAGING.bolApiKey,
Environment.sandbox => SANDBOX.bolApiKey,
Environment.production => throw UnimplementedError(),
};
}
class DropdownMenu extends StatefulWidget {
final List<String> dropDownOptions;
final Function(int) onItemSelected;
const DropdownMenu(
{super.key, required this.dropDownOptions, required this.onItemSelected});
@override
State<DropdownMenu> createState() => _DropdownMenuState();
}
class _DropdownMenuState extends State<DropdownMenu> {
String dropdownValue = '';
@override
void initState() {
super.initState();
dropdownValue = widget.dropDownOptions.first;
}
@override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? value) {
setState(() {
dropdownValue = value!;
});
widget.onItemSelected(widget.dropDownOptions.indexOf(value!));
},
items:
widget.dropDownOptions.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}