Browse Source

allow to read barcode from image

pull/3/head
Khoren Markosyan 3 years ago
parent
commit
d0fc094106
  1. 35
      example/ios/Podfile.lock
  2. 158
      example/lib/pages/creator_page.dart
  3. 141
      example/lib/pages/history_page.dart
  4. 86
      example/lib/pages/scanner_page.dart
  5. 44
      example/pubspec.lock
  6. 1
      example/pubspec.yaml
  7. 49
      ios/Classes/src/native_zxing.cpp
  8. 19
      ios/Classes/src/native_zxing.h
  9. 28
      lib/flutter_zxing.dart
  10. 76
      lib/generated_bindings.dart
  11. 2
      lib/isolate_utils.dart
  12. 1
      lib/zxing_reader_widget.dart
  13. 7
      lib/zxing_writer_widget.dart
  14. 2
      pubspec.yaml

35
example/ios/Podfile.lock

@ -1,50 +1,85 @@
PODS: PODS:
- camera (0.0.1): - camera (0.0.1):
- Flutter - Flutter
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_beep (0.0.1): - flutter_beep (0.0.1):
- Flutter - Flutter
- flutter_zxing (0.0.1): - flutter_zxing (0.0.1):
- Flutter - Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- nb_utils (0.0.1):
- Flutter
- path_provider_ios (0.0.1): - path_provider_ios (0.0.1):
- Flutter - Flutter
- ReachabilitySwift (5.0.0)
- share_plus (0.0.1): - share_plus (0.0.1):
- Flutter - Flutter
- shared_preferences_ios (0.0.1):
- Flutter
- Toast (4.0.0)
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- camera (from `.symlinks/plugins/camera/ios`) - camera (from `.symlinks/plugins/camera/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_beep (from `.symlinks/plugins/flutter_beep/ios`) - flutter_beep (from `.symlinks/plugins/flutter_beep/ios`)
- flutter_zxing (from `.symlinks/plugins/flutter_zxing/ios`) - flutter_zxing (from `.symlinks/plugins/flutter_zxing/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- nb_utils (from `.symlinks/plugins/nb_utils/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- ReachabilitySwift
- Toast
EXTERNAL SOURCES: EXTERNAL SOURCES:
camera: camera:
:path: ".symlinks/plugins/camera/ios" :path: ".symlinks/plugins/camera/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_beep: flutter_beep:
:path: ".symlinks/plugins/flutter_beep/ios" :path: ".symlinks/plugins/flutter_beep/ios"
flutter_zxing: flutter_zxing:
:path: ".symlinks/plugins/flutter_zxing/ios" :path: ".symlinks/plugins/flutter_zxing/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
nb_utils:
:path: ".symlinks/plugins/nb_utils/ios"
path_provider_ios: path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios" :path: ".symlinks/plugins/path_provider_ios/ios"
share_plus: share_plus:
:path: ".symlinks/plugins/share_plus/ios" :path: ".symlinks/plugins/share_plus/ios"
shared_preferences_ios:
:path: ".symlinks/plugins/shared_preferences_ios/ios"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
camera: 9993f92f2c793e87b65e35f3a23c70582afb05b1 camera: 9993f92f2c793e87b65e35f3a23c70582afb05b1
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_beep: 54fb393b22dfa0f0e4573c81b1c74dd71c4e5af8 flutter_beep: 54fb393b22dfa0f0e4573c81b1c74dd71c4e5af8
flutter_zxing: 19a866d17c8a87ee1026d68521c69d2f008635f6 flutter_zxing: 19a866d17c8a87ee1026d68521c69d2f008635f6
fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
nb_utils: ada4338858d8827ec92fdab2a545206b4ba4cfb1
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c

158
example/lib/pages/creator_page.dart

@ -1,13 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:flutter_zxing/generated_bindings.dart';
import 'package:flutter_zxing/zxing_reader_widget.dart';
import 'package:flutter_zxing/zxing_writer_widget.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
@ -23,22 +18,12 @@ class CreatorPage extends StatefulWidget {
State<CreatorPage> createState() => _CreatorPageState(); State<CreatorPage> createState() => _CreatorPageState();
} }
class _CreatorPageState extends State<CreatorPage> class _CreatorPageState extends State<CreatorPage> {
with TickerProviderStateMixin {
CameraController? controller;
TabController? _tabController;
bool isAndroid() => Theme.of(context).platform == TargetPlatform.android; bool isAndroid() => Theme.of(context).platform == TargetPlatform.android;
// Scan result queue
final _resultQueue = <CodeResult>[];
// Write result // Write result
Uint8List? writeResult; Uint8List? writeResult;
// true when the camera is active
bool _isScanning = true;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -47,92 +32,47 @@ class _CreatorPageState extends State<CreatorPage>
} }
void initStateAsync() async { void initStateAsync() async {
_tabController = TabController(length: 3, vsync: this);
_tabController?.addListener(() {
_isScanning = _tabController?.index == 0;
if (_isScanning) {
controller?.resumePreview();
} else {
controller?.pausePreview();
}
});
getTemporaryDirectory().then((value) { getTemporaryDirectory().then((value) {
tempDir = value; tempDir = value;
}); });
} }
@override
void dispose() {
super.dispose();
controller?.dispose();
}
void showInSnackBar(String message) {}
void logError(String code, String? message) {
if (message != null) {
debugPrint('Error: $code\nError Message: $message');
} else {
debugPrint('Error: $code');
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Creator'), title: const Text('Creator'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Scanner'),
Tab(text: 'Result'),
Tab(text: 'Writer'),
],
),
), ),
body: TabBarView( body: SingleChildScrollView(
controller: _tabController, child: Column(
children: [ children: [
// Scanner ZxingWriterWidget(
ZxingReaderWidget(onScan: (result) async { onSuccess: (result) {
_resultQueue.insert(0, result); setState(() {
_tabController?.index = 1; writeResult = result;
await Future.delayed(const Duration(milliseconds: 500)); });
setState(() {}); },
}), onError: (error) {
// Result setState(() {
_buildResultList(), writeResult = null;
// Writer });
SingleChildScrollView( ScaffoldMessenger.of(context).hideCurrentSnackBar();
child: Column( ScaffoldMessenger.of(context).showSnackBar(
children: [ SnackBar(
ZxingWriterWidget( content: Padding(
onSuccess: (result) { padding: const EdgeInsets.only(bottom: 30.0),
setState(() { child: Text(
writeResult = result; error,
}); textAlign: TextAlign.center,
},
onError: (error) {
setState(() {
writeResult = null;
});
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
error,
textAlign: TextAlign.center,
),
), ),
); ),
}, ),
), );
if (writeResult != null) buildWriteResult(), },
],
), ),
), if (writeResult != null) buildWriteResult(),
], ],
),
), ),
); );
} }
@ -157,44 +97,4 @@ class _CreatorPageState extends State<CreatorPage>
], ],
); );
} }
_buildResultList() {
return _resultQueue.isEmpty
? const Center(
child: Text(
'No Results',
style: TextStyle(fontSize: 24),
))
: ListView.builder(
itemCount: _resultQueue.length,
itemBuilder: (context, index) {
final result = _resultQueue[index];
return ListTile(
title: Text(result.textString),
subtitle: Text(result.formatString),
trailing: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: [
// Copy button
TextButton(
child: const Text('Copy'),
onPressed: () {
Clipboard.setData(
ClipboardData(text: result.textString));
},
),
// Remove button
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () {
_resultQueue.removeAt(index);
setState(() {});
},
),
],
),
);
},
);
}
} }

141
example/lib/pages/history_page.dart

@ -1,18 +1,6 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:flutter_zxing/generated_bindings.dart';
import 'package:flutter_zxing/zxing_reader_widget.dart';
import 'package:flutter_zxing/zxing_writer_widget.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
late Directory tempDir;
String get tempPath => '${tempDir.path}/zxing.jpg';
class HistoryPage extends StatefulWidget { class HistoryPage extends StatefulWidget {
const HistoryPage({ const HistoryPage({
@ -23,137 +11,16 @@ class HistoryPage extends StatefulWidget {
State<HistoryPage> createState() => _HistoryPageState(); State<HistoryPage> createState() => _HistoryPageState();
} }
class _HistoryPageState extends State<HistoryPage> with TickerProviderStateMixin { class _HistoryPageState extends State<HistoryPage> {
CameraController? controller;
TabController? _tabController;
bool isAndroid() => Theme.of(context).platform == TargetPlatform.android;
// Scan result queue
final _resultQueue = <CodeResult>[]; final _resultQueue = <CodeResult>[];
// Write result
Uint8List? writeResult;
// true when the camera is active
bool _isScanning = true;
@override
void initState() {
super.initState();
initStateAsync();
}
void initStateAsync() async {
_tabController = TabController(length: 3, vsync: this);
_tabController?.addListener(() {
_isScanning = _tabController?.index == 0;
if (_isScanning) {
controller?.resumePreview();
} else {
controller?.pausePreview();
}
});
getTemporaryDirectory().then((value) {
tempDir = value;
});
}
@override
void dispose() {
super.dispose();
controller?.dispose();
}
void showInSnackBar(String message) {}
void logError(String code, String? message) {
if (message != null) {
debugPrint('Error: $code\nError Message: $message');
} else {
debugPrint('Error: $code');
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Flutter Scanner'), title: const Text('History'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Scanner'),
Tab(text: 'Result'),
Tab(text: 'Writer'),
],
),
), ),
body: TabBarView( body: _buildResultList(),
controller: _tabController,
children: [
// Scanner
ZxingReaderWidget(onScan: (result) async {
_resultQueue.insert(0, result);
_tabController?.index = 1;
await Future.delayed(const Duration(milliseconds: 500));
setState(() {});
}),
// Result
_buildResultList(),
// Writer
SingleChildScrollView(
child: Column(
children: [
ZxingWriterWidget(
onSuccess: (result) {
setState(() {
writeResult = result;
});
},
onError: (error) {
setState(() {
writeResult = null;
});
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
error,
textAlign: TextAlign.center,
),
),
);
},
),
if (writeResult != null) buildWriteResult(),
],
),
),
],
),
);
}
Column buildWriteResult() {
return Column(
children: [
// Barcode image
Image.memory(writeResult ?? Uint8List(0)),
// Share button
ElevatedButton(
onPressed: () {
// Save image to device
final file = File(tempPath);
file.writeAsBytesSync(writeResult ?? Uint8List(0));
final path = file.path;
// Share image
Share.shareFiles([path]);
},
child: const Text('Share'),
),
],
); );
} }
@ -169,7 +36,7 @@ class _HistoryPageState extends State<HistoryPage> with TickerProviderStateMixin
itemBuilder: (context, index) { itemBuilder: (context, index) {
final result = _resultQueue[index]; final result = _resultQueue[index];
return ListTile( return ListTile(
title: Text(result.textString), title: Text(result.textString ?? ''),
subtitle: Text(result.formatString), subtitle: Text(result.formatString),
trailing: ButtonBar( trailing: ButtonBar(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

86
example/lib/pages/scanner_page.dart

@ -1,5 +1,10 @@
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_zxing/zxing_reader_widget.dart'; import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image/image.dart' as imglib;
class ScannerPage extends StatefulWidget { class ScannerPage extends StatefulWidget {
const ScannerPage({ const ScannerPage({
@ -10,60 +15,45 @@ class ScannerPage extends StatefulWidget {
State<ScannerPage> createState() => _ScannerPageState(); State<ScannerPage> createState() => _ScannerPageState();
} }
class _ScannerPageState extends State<ScannerPage> class _ScannerPageState extends State<ScannerPage> {
with TickerProviderStateMixin { final ImagePicker _picker = ImagePicker();
TabController? _tabController;
@override
void initState() {
super.initState();
initStateAsync();
}
void initStateAsync() async {
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
super.dispose();
_tabController?.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Scanner'), title: const Text('Scanner'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Single code'),
Tab(text: 'Multi code'),
Tab(text: 'Image'),
],
),
), ),
body: TabBarView( body: ZxingReaderWidget(
controller: _tabController, onScan: (result) async {
children: [ // _resultQueue.insert(0, result);
// Single code scanner // await Future.delayed(const Duration(milliseconds: 500));
ZxingReaderWidget(onScan: (result) async { // setState(() {});
// _resultQueue.insert(0, result); },
// await Future.delayed(const Duration(milliseconds: 500)); ),
// setState(() {}); floatingActionButton: FloatingActionButton(
}), child: const Icon(FontAwesomeIcons.image),
// Multi code scanner onPressed: () async {
Container(), final XFile? file =
// ZxingReaderWidget(onScan: (result) async { await _picker.pickImage(source: ImageSource.gallery);
// // _resultQueue.insert(0, result); if (file != null) {
// // await Future.delayed(const Duration(milliseconds: 500)); final Uint8List bytes = await file.readAsBytes();
// // setState(() {}); imglib.Image? image = imglib.decodeImage(bytes);
// }), if (image != null) {
// Image scanner final CodeResult result = FlutterZxing.readBarcode(
Container(), image.getBytes(format: imglib.Format.luminance),
], Format.Any,
image.width,
image.height,
0,
0,
);
if (result.isValidBool) {
debugPrint(result.textString);
}
}
}
},
), ),
); );
} }

44
example/pubspec.lock

@ -105,7 +105,7 @@ packages:
name: camera name: camera
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.4+19" version: "0.9.4+21"
camera_platform_interface: camera_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -378,6 +378,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.4"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
@ -399,6 +406,41 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
image_picker:
dependency: "direct main"
description:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.5"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.4+11"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.6"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.5"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.4"
intl: intl:
dependency: transitive dependency: transitive
description: description:

1
example/pubspec.yaml

@ -18,6 +18,7 @@ dependencies:
flutter_zxing: flutter_zxing:
path: ../ path: ../
font_awesome_flutter: ^10.1.0 font_awesome_flutter: ^10.1.0
image_picker: ^0.8.5
nb_utils: ^4.5.0 nb_utils: ^4.5.0
path_provider: ^2.0.9 path_provider: ^2.0.9
share_plus: ^4.0.4 share_plus: ^4.0.4

49
ios/Classes/src/native_zxing.cpp

@ -10,13 +10,13 @@ using namespace ZXing;
extern "C" extern "C"
{ {
FUNCTION_ATTRIBUTE FUNCTION_ATTRIBUTE
char *zxingVersion() char *version()
{ {
return "1.3.0"; return "1.3.0";
} }
FUNCTION_ATTRIBUTE FUNCTION_ATTRIBUTE
struct CodeResult zxingRead(char *bytes, int format, int width, int height, int cropWidth, int cropHeight, int logEnabled) struct CodeResult readBarcode(char *bytes, int format, int width, int height, int cropWidth, int cropHeight, int logEnabled)
{ {
long long start = get_now(); long long start = get_now();
@ -52,7 +52,50 @@ extern "C"
} }
FUNCTION_ATTRIBUTE FUNCTION_ATTRIBUTE
struct EncodeResult zxingEncode(char *contents, int width, int height, int format, int margin, int eccLevel, int logEnabled) struct CodeResult* readBarcodes(char *bytes, int format, int width, int height, int cropWidth, int cropHeight, int logEnabled)
{
long long start = get_now();
long length = width * height;
uint8_t *data = new uint8_t[length];
memcpy(data, bytes, length);
BarcodeFormats formats = BarcodeFormat(format); // BarcodeFormat::Any;
DecodeHints hints = DecodeHints().setTryHarder(false).setTryRotate(true).setFormats(formats);
ImageView image{data, width, height, ImageFormat::Lum};
if (cropWidth > 0 && cropHeight > 0 && cropWidth < width && cropHeight < height)
{
image = image.cropped(width / 2 - cropWidth / 2, height / 2 - cropHeight / 2, cropWidth, cropHeight);
}
Results results = ReadBarcodes(image, hints);
struct CodeResult *codes = new struct CodeResult [results.size()];
int i = 0;
for (auto &result : results)
{
struct CodeResult code = {false, nullptr};
if (result.isValid())
{
code.isValid = result.isValid();
code.text = new char[result.text().length() + 1];
std::string text = std::string(result.text().begin(), result.text().end());
strcpy(code.text, text.c_str());
code.format = Format(static_cast<int>(result.format()));
codes[i] = code;
i++;
}
}
int evalInMillis = static_cast<int>(get_now() - start);
if (logEnabled)
{
platform_log("zxingRead: %d ms", evalInMillis);
}
return codes;
}
FUNCTION_ATTRIBUTE
struct EncodeResult encodeBarcode(char *contents, int width, int height, int format, int margin, int eccLevel, int logEnabled)
{ {
long long start = get_now(); long long start = get_now();

19
ios/Classes/src/native_zxing.h

@ -48,7 +48,7 @@ extern "C"
* *
* @return The version of the zxing library. * @return The version of the zxing library.
*/ */
char *zxingVersion(); char *version();
/** /**
* @brief Reads barcode from image. * @brief Reads barcode from image.
@ -61,7 +61,20 @@ extern "C"
* @param logEnabled Log enabled. * @param logEnabled Log enabled.
* @return Barcode result. * @return Barcode result.
*/ */
struct CodeResult zxingRead(char *bytes, int format, int width, int height, int cropWidth, int cropHeight, int logEnabled); struct CodeResult readBarcode(char *bytes, int format, int width, int height, int cropWidth, int cropHeight, int logEnabled);
/**
* @brief Reads barcodes from image.
* @param bytes Image bytes.
* @param format The format of the barcode
* @param width Image width.
* @param height Image height.
* @param cropWidth Crop width.
* @param cropHeight Crop height.
* @param logEnabled Log enabled.
* @return Barcode results.
*/
struct CodeResult* readBarcodes(char *bytes, int format, int width, int height, int cropWidth, int cropHeight, int logEnabled);
/** /**
* @brief Encode a string into a barcode * @brief Encode a string into a barcode
@ -74,7 +87,7 @@ extern "C"
* @param eccLevel The error correction level of the barcode. Used for Aztec, PDF417, and QRCode only, [0-8]. * @param eccLevel The error correction level of the barcode. Used for Aztec, PDF417, and QRCode only, [0-8].
* @return The barcode data * @return The barcode data
*/ */
struct EncodeResult zxingEncode(char *contents, int width, int height, int format, int margin, int eccLevel, int logEnabled); struct EncodeResult encodeBarcode(char *contents, int width, int height, int format, int margin, int eccLevel, int logEnabled);
#ifdef __cplusplus #ifdef __cplusplus
} }

28
lib/flutter_zxing.dart

@ -4,10 +4,15 @@ import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'generated_bindings.dart'; import 'generated_bindings.dart';
export 'generated_bindings.dart';
export 'zxing_reader_widget.dart';
export 'zxing_writer_widget.dart';
class FlutterZxing { class FlutterZxing {
static const MethodChannel _channel = MethodChannel('flutter_zxing'); static const MethodChannel _channel = MethodChannel('flutter_zxing');
@ -20,19 +25,27 @@ class FlutterZxing {
static bool logEnabled = true; static bool logEnabled = true;
static String zxingVersion() { static String version() {
return bindings.zxingVersion().cast<Utf8>().toDartString(); return bindings.version().cast<Utf8>().toDartString();
} }
static CodeResult zxingRead(Uint8List bytes, int format, int width, static CodeResult readBarcode(Uint8List bytes, int format, int width,
int height, int cropWidth, int cropHeight) { int height, int cropWidth, int cropHeight) {
return bindings.zxingRead(bytes.allocatePointer(), format, width, height, return bindings.readBarcode(bytes.allocatePointer(), format, width, height,
cropWidth, cropHeight, _logEnabled); cropWidth, cropHeight, _logEnabled);
} }
static EncodeResult zxingEncode(String contents, int width, int height, static List<CodeResult> readBarcodes(Uint8List bytes, int format, int width,
int height, int cropWidth, int cropHeight) {
final result = bindings.readBarcodes(bytes.allocatePointer(), format, width,
height, cropWidth, cropHeight, _logEnabled);
debugPrint(result.toString());
return [];
}
static EncodeResult encodeBarcode(String contents, int width, int height,
int format, int margin, int eccLevel) { int format, int margin, int eccLevel) {
var result = bindings.zxingEncode(contents.toNativeUtf8().cast<Int8>(), var result = bindings.encodeBarcode(contents.toNativeUtf8().cast<Int8>(),
width, height, format, margin, eccLevel, _logEnabled); width, height, format, margin, eccLevel, _logEnabled);
return result; return result;
} }
@ -70,7 +83,8 @@ extension Encode on EncodeResult {
extension Code on CodeResult { extension Code on CodeResult {
bool get isValidBool => isValid == 1; bool get isValidBool => isValid == 1;
String get textString => text.cast<Utf8>().toDartString(); String? get textString =>
text == nullptr ? null : text.cast<Utf8>().toDartString();
String get formatString { String get formatString {
return CodeFormat.formatName(format); return CodeFormat.formatName(format);

76
lib/generated_bindings.dart

@ -22,15 +22,14 @@ class GeneratedBindings {
/// Returns the version of the zxing library. /// Returns the version of the zxing library.
/// ///
/// @return The version of the zxing library. /// @return The version of the zxing library.
ffi.Pointer<ffi.Int8> zxingVersion() { ffi.Pointer<ffi.Int8> version() {
return _zxingVersion(); return _version();
} }
late final _zxingVersionPtr = late final _versionPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Int8> Function()>>( _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Int8> Function()>>('version');
'zxingVersion'); late final _version =
late final _zxingVersion = _versionPtr.asFunction<ffi.Pointer<ffi.Int8> Function()>();
_zxingVersionPtr.asFunction<ffi.Pointer<ffi.Int8> Function()>();
/// @brief Reads barcode from image. /// @brief Reads barcode from image.
/// @param bytes Image bytes. /// @param bytes Image bytes.
@ -41,7 +40,7 @@ class GeneratedBindings {
/// @param cropHeight Crop height. /// @param cropHeight Crop height.
/// @param logEnabled Log enabled. /// @param logEnabled Log enabled.
/// @return Barcode result. /// @return Barcode result.
CodeResult zxingRead( CodeResult readBarcode(
ffi.Pointer<ffi.Int8> bytes, ffi.Pointer<ffi.Int8> bytes,
int format, int format,
int width, int width,
@ -50,7 +49,7 @@ class GeneratedBindings {
int cropHeight, int cropHeight,
int logEnabled, int logEnabled,
) { ) {
return _zxingRead( return _readBarcode(
bytes, bytes,
format, format,
width, width,
@ -61,14 +60,57 @@ class GeneratedBindings {
); );
} }
late final _zxingReadPtr = _lookup< late final _readBarcodePtr = _lookup<
ffi.NativeFunction< ffi.NativeFunction<
CodeResult Function(ffi.Pointer<ffi.Int8>, ffi.Int32, ffi.Int32, CodeResult Function(ffi.Pointer<ffi.Int8>, ffi.Int32, ffi.Int32,
ffi.Int32, ffi.Int32, ffi.Int32, ffi.Int32)>>('zxingRead'); ffi.Int32, ffi.Int32, ffi.Int32, ffi.Int32)>>('readBarcode');
late final _zxingRead = _zxingReadPtr.asFunction< late final _readBarcode = _readBarcodePtr.asFunction<
CodeResult Function( CodeResult Function(
ffi.Pointer<ffi.Int8>, int, int, int, int, int, int)>(); ffi.Pointer<ffi.Int8>, int, int, int, int, int, int)>();
/// @brief Reads barcodes from image.
/// @param bytes Image bytes.
/// @param format The format of the barcode
/// @param width Image width.
/// @param height Image height.
/// @param cropWidth Crop width.
/// @param cropHeight Crop height.
/// @param logEnabled Log enabled.
/// @return Barcode results.
ffi.Pointer<CodeResult> readBarcodes(
ffi.Pointer<ffi.Int8> bytes,
int format,
int width,
int height,
int cropWidth,
int cropHeight,
int logEnabled,
) {
return _readBarcodes(
bytes,
format,
width,
height,
cropWidth,
cropHeight,
logEnabled,
);
}
late final _readBarcodesPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<CodeResult> Function(
ffi.Pointer<ffi.Int8>,
ffi.Int32,
ffi.Int32,
ffi.Int32,
ffi.Int32,
ffi.Int32,
ffi.Int32)>>('readBarcodes');
late final _readBarcodes = _readBarcodesPtr.asFunction<
ffi.Pointer<CodeResult> Function(
ffi.Pointer<ffi.Int8>, int, int, int, int, int, int)>();
/// @brief Encode a string into a barcode /// @brief Encode a string into a barcode
/// @param contents The string to encode /// @param contents The string to encode
/// @param width The width of the barcode /// @param width The width of the barcode
@ -78,7 +120,7 @@ class GeneratedBindings {
/// @param logEnabled Log enabled. /// @param logEnabled Log enabled.
/// @param eccLevel The error correction level of the barcode. Used for Aztec, PDF417, and QRCode only, [0-8]. /// @param eccLevel The error correction level of the barcode. Used for Aztec, PDF417, and QRCode only, [0-8].
/// @return The barcode data /// @return The barcode data
EncodeResult zxingEncode( EncodeResult encodeBarcode(
ffi.Pointer<ffi.Int8> contents, ffi.Pointer<ffi.Int8> contents,
int width, int width,
int height, int height,
@ -87,7 +129,7 @@ class GeneratedBindings {
int eccLevel, int eccLevel,
int logEnabled, int logEnabled,
) { ) {
return _zxingEncode( return _encodeBarcode(
contents, contents,
width, width,
height, height,
@ -98,11 +140,11 @@ class GeneratedBindings {
); );
} }
late final _zxingEncodePtr = _lookup< late final _encodeBarcodePtr = _lookup<
ffi.NativeFunction< ffi.NativeFunction<
EncodeResult Function(ffi.Pointer<ffi.Int8>, ffi.Int32, ffi.Int32, EncodeResult Function(ffi.Pointer<ffi.Int8>, ffi.Int32, ffi.Int32,
ffi.Int32, ffi.Int32, ffi.Int32, ffi.Int32)>>('zxingEncode'); ffi.Int32, ffi.Int32, ffi.Int32, ffi.Int32)>>('encodeBarcode');
late final _zxingEncode = _zxingEncodePtr.asFunction< late final _encodeBarcode = _encodeBarcodePtr.asFunction<
EncodeResult Function( EncodeResult Function(
ffi.Pointer<ffi.Int8>, int, int, int, int, int, int)>(); ffi.Pointer<ffi.Int8>, int, int, int, int, int, int)>();
} }

2
lib/isolate_utils.dart

@ -63,7 +63,7 @@ class IsolateUtils {
final cropSize = final cropSize =
(min(image.width, image.height) * cropPercent).round(); (min(image.width, image.height) * cropPercent).round();
final result = FlutterZxing.zxingRead(bytes, isolateData.format, final result = FlutterZxing.readBarcode(bytes, isolateData.format,
image.width, image.height, cropSize, cropSize); image.width, image.height, cropSize, cropSize);
isolateData.responsePort?.send(result); isolateData.responsePort?.send(result);

1
lib/zxing_reader_widget.dart

@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_beep/flutter_beep.dart'; import 'package:flutter_beep/flutter_beep.dart';
import 'flutter_zxing.dart'; import 'flutter_zxing.dart';
import 'generated_bindings.dart';
import 'isolate_utils.dart'; import 'isolate_utils.dart';
import 'scanner_overlay.dart'; import 'scanner_overlay.dart';

7
lib/zxing_writer_widget.dart

@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:image/image.dart' as imglib; import 'package:image/image.dart' as imglib;
import 'flutter_zxing.dart'; import 'flutter_zxing.dart';
import 'generated_bindings.dart';
class ZxingWriterWidget extends StatefulWidget { class ZxingWriterWidget extends StatefulWidget {
const ZxingWriterWidget({ const ZxingWriterWidget({
@ -71,7 +70,7 @@ class _ZxingWriterWidgetState extends State<ZxingWriterWidget>
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
filled: true, filled: true,
hintText: 'Enter text to encode', hintText: 'Enter text to create a barcode',
counterText: counterText:
'${_textController.value.text.length} / $_maxTextLength', '${_textController.value.text.length} / $_maxTextLength',
), ),
@ -85,7 +84,7 @@ class _ZxingWriterWidgetState extends State<ZxingWriterWidget>
final text = _textController.value.text; final text = _textController.value.text;
const width = 300; const width = 300;
const height = 300; const height = 300;
final result = FlutterZxing.zxingEncode( final result = FlutterZxing.encodeBarcode(
text, width, height, _codeFormat, 5, 0); text, width, height, _codeFormat, 5, 0);
String? error; String? error;
if (result.isValidBool) { if (result.isValidBool) {
@ -107,7 +106,7 @@ class _ZxingWriterWidgetState extends State<ZxingWriterWidget>
} }
} }
}, },
child: const Text('Encode'), child: const Text('Create'),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
], ],

2
pubspec.yaml

@ -8,7 +8,7 @@ environment:
flutter: ">=2.5.0" flutter: ">=2.5.0"
dependencies: dependencies:
camera: 0.9.4+19 camera: ^0.9.4
ffi: ^1.1.2 ffi: ^1.1.2
flutter: flutter:
sdk: flutter sdk: flutter

Loading…
Cancel
Save