Browse Source

code refactoring, fixed building for web

pull/62/head
Khoren Markosyan 2 years ago
parent
commit
6f9ff88221
  1. 39
      example/.metadata
  2. 6
      example/android/app/src/main/kotlin/com/markosyan/example/MainActivity.kt
  3. 66
      example/lib/main.dart
  4. BIN
      example/web/favicon.png
  5. BIN
      example/web/icons/Icon-192.png
  6. BIN
      example/web/icons/Icon-512.png
  7. BIN
      example/web/icons/Icon-maskable-192.png
  8. BIN
      example/web/icons/Icon-maskable-512.png
  9. 58
      example/web/index.html
  10. 35
      example/web/manifest.json
  11. 4
      ios/Classes/src/native_zxing.cpp
  12. 35
      ios/Classes/src/native_zxing.h
  13. 105
      lib/flutter_zxing.dart
  14. 61
      lib/generated_bindings.dart
  15. 22
      lib/src/logic/barcode_encoder.dart
  16. 78
      lib/src/logic/barcode_reader.dart
  17. 74
      lib/src/logic/barcodes_reader.dart
  18. 18
      lib/src/logic/camera_stream.dart
  19. 13
      lib/src/logic/zxing.dart
  20. 20
      lib/src/models/code.dart
  21. 20
      lib/src/models/encode.dart
  22. 82
      lib/src/models/format.dart
  23. 1
      lib/src/models/messages.dart
  24. 6
      lib/src/models/models.dart
  25. 22
      lib/src/models/params.dart
  26. 22
      lib/src/models/position.dart
  27. 29
      lib/src/ui/dynamic_scanner_overlay.dart
  28. 38
      lib/src/ui/fixed_scanner_overlay.dart
  29. 30
      lib/src/ui/reader_widget.dart
  30. 5
      lib/src/ui/ui.dart
  31. 18
      lib/src/ui/writer_widget.dart
  32. 100
      lib/src/utils/extentions.dart
  33. 34
      lib/src/utils/isolate_utils.dart
  34. 4
      lib/zxing_cross.dart
  35. 149
      lib/zxing_mobile.dart
  36. 104
      lib/zxing_web.dart
  37. 2
      zxscanner/pubspec.lock

39
example/.metadata

@ -1,10 +1,45 @@
# This file tracks properties of this Flutter project. # This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc. # Used by Flutter tool to assess capabilities and perform upgrades etc.
# #
# This file should be version controlled and should not be manually edited. # This file should be version controlled.
version: version:
revision: 7e9793dee1b85a243edd0e06cb1658e98b077561 revision: 135454af32477f815a7525073027a3ff9eff1bfd
channel: stable channel: stable
project_type: app project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: android
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: ios
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: linux
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: macos
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: web
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
- platform: windows
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

6
example/android/app/src/main/kotlin/com/markosyan/example/MainActivity.kt

@ -0,0 +1,6 @@
package com.markosyan.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

66
example/lib/main.dart

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:flutter_zxing/flutter_zxing.dart';
void main() { void main() {
setZxingLogEnabled(kDebugMode); zx.setLogEnabled(kDebugMode);
runApp(const MyApp()); runApp(const MyApp());
} }
@ -51,31 +51,47 @@ class _DemoPageState extends State<DemoPage> {
body: TabBarView( body: TabBarView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: [ children: [
ReaderWidget( if (kIsWeb)
onScan: (value) { Center(
showMessage(context, 'Scanned: ${value.textString ?? ''}'); child: Text(
}, 'Web is not supported yet.',
tryInverted: true, style: Theme.of(context).textTheme.headline6,
), ),
ListView( )
children: [ else
WriterWidget( ReaderWidget(
messages: const Messages( onScan: (value) {
createButton: 'Create Code', showMessage(context, 'Scanned: ${value.text ?? ''}');
), },
onSuccess: (result, bytes) { tryInverted: true,
setState(() { ),
createdCodeBytes = bytes; if (kIsWeb)
}); Center(
}, child: Text(
onError: (error) { 'Web is not supported yet.',
showMessage(context, 'Error: $error'); style: Theme.of(context).textTheme.headline6,
},
), ),
if (createdCodeBytes != null) )
Image.memory(createdCodeBytes ?? Uint8List(0), height: 200), else
], ListView(
), children: [
WriterWidget(
messages: const Messages(
createButton: 'Create Code',
),
onSuccess: (result, bytes) {
setState(() {
createdCodeBytes = bytes;
});
},
onError: (error) {
showMessage(context, 'Error: $error');
},
),
if (createdCodeBytes != null)
Image.memory(createdCodeBytes ?? Uint8List(0), height: 200),
],
),
], ],
), ),
), ),

BIN
example/web/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

BIN
example/web/icons/Icon-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
example/web/icons/Icon-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
example/web/icons/Icon-maskable-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
example/web/icons/Icon-maskable-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

58
example/web/index.html

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
}
}).then(function(engineInitializer) {
return engineInitializer.initializeEngine();
}).then(function(appRunner) {
return appRunner.runApp();
});
});
</script>
</body>
</html>

35
example/web/manifest.json

@ -0,0 +1,35 @@
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

4
ios/Classes/src/native_zxing.cpp

@ -94,7 +94,7 @@ extern "C"
{ {
long long start = get_now(); long long start = get_now();
struct EncodeResult result = {0, contents, Format(format), nullptr, 0, nullptr}; struct EncodeResult result = {0, contents, format, nullptr, 0, nullptr};
try try
{ {
auto writer = MultiFormatWriter(BarcodeFormat(format)).setMargin(margin).setEccLevel(eccLevel).setEncoding(CharacterSet::UTF8); auto writer = MultiFormatWriter(BarcodeFormat(format)).setMargin(margin).setEccLevel(eccLevel).setEncoding(CharacterSet::UTF8);
@ -120,7 +120,7 @@ extern "C"
{ {
code->isValid = result.isValid(); code->isValid = result.isValid();
code->format = Format(static_cast<int>(result.format())); code->format = static_cast<int>(result.format());
code->bytes = result.bytes().data(); code->bytes = result.bytes().data();
code->length = result.bytes().size(); code->length = result.bytes().size();

35
ios/Classes/src/native_zxing.h

@ -3,35 +3,6 @@ extern "C"
{ {
#endif #endif
/**
* @brief Format Enumerates barcode formats known to this package.
*
*/
enum Format
{
None = 0, ///< Used as a return value if no valid barcode has been detected
Aztec = (1 << 0), ///< Aztec (2D)
Codabar = (1 << 1), ///< Codabar (1D)
Code39 = (1 << 2), ///< Code39 (1D)
Code93 = (1 << 3), ///< Code93 (1D)
Code128 = (1 << 4), ///< Code128 (1D)
DataBar = (1 << 5), ///< GS1 DataBar, formerly known as RSS 14
DataBarExpanded = (1 << 6), ///< GS1 DataBar Expanded, formerly known as RSS EXPANDED
DataMatrix = (1 << 7), ///< DataMatrix (2D)
EAN8 = (1 << 8), ///< EAN-8 (1D)
EAN13 = (1 << 9), ///< EAN-13 (1D)
ITF = (1 << 10), ///< ITF (Interleaved Two of Five) (1D)
MaxiCode = (1 << 11), ///< MaxiCode (2D)
PDF417 = (1 << 12), ///< PDF417 (1D) or (2D)
QRCode = (1 << 13), ///< QR Code (2D)
UPCA = (1 << 14), ///< UPC-A (1D)
UPCE = (1 << 15), ///< UPC-E (1D)
OneDCodes = Codabar | Code39 | Code93 | Code128 | EAN8 | EAN13 | ITF | DataBar | DataBarExpanded | UPCA | UPCE,
TwoDCodes = Aztec | DataMatrix | MaxiCode | PDF417 | QRCode,
Any = OneDCodes | TwoDCodes,
};
/** /**
* @brief Pos is a position of a barcode in a image. * @brief Pos is a position of a barcode in a image.
* *
@ -57,7 +28,7 @@ extern "C"
char *text; ///< The decoded text char *text; ///< The decoded text
const unsigned char *bytes; ///< The bytes is the raw / standard content without any modifications like character set conversions const unsigned char *bytes; ///< The bytes is the raw / standard content without any modifications like character set conversions
int length; ///< The length of the bytes int length; ///< The length of the bytes
enum Format format; ///< The format of the barcode int format; ///< The format of the barcode
struct Pos *pos; ///< The position of the barcode within the image struct Pos *pos; ///< The position of the barcode within the image
}; };
@ -78,8 +49,8 @@ extern "C"
{ {
int isValid; ///< Whether the barcode was successfully encoded int isValid; ///< Whether the barcode was successfully encoded
char *text; ///< The encoded text char *text; ///< The encoded text
enum Format format; ///< The format of the barcode int format; ///< The format of the barcode
const signed char *data; ///< The encoded data const signed char *data; ///< The encoded data
int length; ///< The length of the encoded data int length; ///< The length of the encoded data
char *error; ///< The error message char *error; ///< The error message
}; };

105
lib/flutter_zxing.dart

@ -1,10 +1,95 @@
export 'generated_bindings.dart'; import 'dart:typed_data';
export 'src/logic/zxing.dart';
export 'src/ui/dynamic_scanner_overlay.dart'; import 'package:camera/camera.dart';
export 'src/ui/fixed_scanner_overlay.dart'; import 'src/models/models.dart';
export 'src/ui/reader_widget.dart';
export 'src/ui/scanner_overlay.dart'; import 'zxing_cross.dart'
export 'src/ui/writer_widget.dart'; if (dart.library.io) 'zxing_mobile.dart'
export 'src/utils/extentions.dart'; if (dart.library.html) 'zxing_web.dart';
export 'src/utils/image_converter.dart';
export 'src/utils/messages.dart'; export 'src/models/models.dart';
export 'src/ui/ui.dart';
final Zxing zx = Zxing();
abstract class Zxing {
/// factory constructor to return the correct implementation.
factory Zxing() => getZxing();
String version() => '';
void setLogEnabled(bool enabled) {}
String barcodeFormatName(int format) => '';
Encode encodeBarcode(
String contents, {
int format = Format.qrCode,
int width = 300,
int height = 300,
int margin = 0,
int eccLevel = 0,
});
/// Starts reading barcode from the camera
Future<void> startCameraProcessing();
/// Stops reading barcode from the camera
void stopCameraProcessing();
/// Reads barcode from the camera
Future<Code> processCameraImage(
CameraImage image, {
Params? params,
});
/// Reads barcode from String image path
Future<Code?> readBarcodeImagePathString(
String path, {
Params? params,
});
/// Reads barcode from XFile image path
Future<Code?> readBarcodeImagePath(
XFile path, {
Params? params,
});
/// Reads barcode from image url
Future<Code?> readBarcodeImageUrl(
String url, {
Params? params,
});
// Reads barcode from Uint8List image bytes
Code readBarcode(
Uint8List bytes, {
required int width,
required int height,
Params? params,
});
/// Reads barcodes from String image path
Future<List<Code>> readBarcodesImagePathString(
String path, {
Params? params,
});
/// Reads barcodes from XFile image path
Future<List<Code>> readBarcodesImagePath(
XFile path, {
Params? params,
});
/// Reads barcodes from image url
Future<List<Code>> readBarcodesImageUrl(
String url, {
Params? params,
});
/// Reads barcodes from Uint8List image bytes
List<Code> readBarcodes(
Uint8List bytes, {
required int width,
required int height,
Params? params,
});
}

61
lib/generated_bindings.dart

@ -162,63 +162,6 @@ class GeneratedBindings {
EncodeResult Function(ffi.Pointer<ffi.Char>, int, int, int, int, int)>(); EncodeResult Function(ffi.Pointer<ffi.Char>, int, int, int, int, int)>();
} }
/// @brief Format Enumerates barcode formats known to this package.
abstract class Format {
/// < Used as a return value if no valid barcode has been detected
static const int None = 0;
/// < Aztec (2D)
static const int Aztec = 1;
/// < Codabar (1D)
static const int Codabar = 2;
/// < Code39 (1D)
static const int Code39 = 4;
/// < Code93 (1D)
static const int Code93 = 8;
/// < Code128 (1D)
static const int Code128 = 16;
/// < GS1 DataBar, formerly known as RSS 14
static const int DataBar = 32;
/// < GS1 DataBar Expanded, formerly known as RSS EXPANDED
static const int DataBarExpanded = 64;
/// < DataMatrix (2D)
static const int DataMatrix = 128;
/// < EAN-8 (1D)
static const int EAN8 = 256;
/// < EAN-13 (1D)
static const int EAN13 = 512;
/// < ITF (Interleaved Two of Five) (1D)
static const int ITF = 1024;
/// < MaxiCode (2D)
static const int MaxiCode = 2048;
/// < PDF417 (1D) or (2D)
static const int PDF417 = 4096;
/// < QR Code (2D)
static const int QRCode = 8192;
/// < UPC-A (1D)
static const int UPCA = 16384;
/// < UPC-E (1D)
static const int UPCE = 32768;
static const int OneDCodes = 51070;
static const int TwoDCodes = 14465;
static const int Any = 65535;
}
/// @brief Pos is a position of a barcode in a image. /// @brief Pos is a position of a barcode in a image.
class Pos extends ffi.Struct { class Pos extends ffi.Struct {
/// < x coordinate of top left corner of barcode /// < x coordinate of top left corner of barcode
@ -271,7 +214,7 @@ class CodeResult extends ffi.Struct {
external int length; external int length;
/// < The format of the barcode /// < The format of the barcode
@ffi.Int32() @ffi.Int()
external int format; external int format;
/// < The position of the barcode within the image /// < The position of the barcode within the image
@ -298,7 +241,7 @@ class EncodeResult extends ffi.Struct {
external ffi.Pointer<ffi.Char> text; external ffi.Pointer<ffi.Char> text;
/// < The format of the barcode /// < The format of the barcode
@ffi.Int32() @ffi.Int()
external int format; external int format;
/// < The encoded data /// < The encoded data

22
lib/src/logic/barcode_encoder.dart

@ -1,20 +1,22 @@
part of 'zxing.dart'; part of 'zxing.dart';
// Encode a string into a barcode // Encode a string into a barcode
EncodeResult encodeBarcode( Encode zxingEncodeBarcode(
String contents, { String contents, {
int format = Format.QRCode, int format = Format.qrCode,
int width = 300, int width = 300,
int height = 300, int height = 300,
int margin = 0, int margin = 0,
int eccLevel = 0, int eccLevel = 0,
}) { }) {
return bindings.encodeBarcode( return bindings
contents.toNativeUtf8().cast<Char>(), .encodeBarcode(
width, contents.toNativeUtf8().cast<Char>(),
height, width,
format, height,
margin, format,
eccLevel, margin,
); eccLevel,
)
.toEncode();
} }

78
lib/src/logic/barcode_reader.dart

@ -1,57 +1,37 @@
part of 'zxing.dart'; part of 'zxing.dart';
/// Reads barcode from String image path /// Reads barcode from String image path
Future<CodeResult?> readBarcodeImagePathString( Future<Code?> zxingReadBarcodeImagePathString(
String path, { String path, {
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) => }) =>
readBarcodeImagePath( zxingReadBarcodeImagePath(
XFile(path), XFile(path),
format: format, params: params,
cropWidth: cropWidth,
cropHeight: cropHeight,
tryHarder: tryHarder,
tryRotate: tryRotate,
); );
/// Reads barcode from XFile image path /// Reads barcode from XFile image path
Future<CodeResult?> readBarcodeImagePath( Future<Code?> zxingReadBarcodeImagePath(
XFile path, { XFile path, {
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) async { }) async {
final Uint8List imageBytes = await path.readAsBytes(); final Uint8List imageBytes = await path.readAsBytes();
final imglib.Image? image = imglib.decodeImage(imageBytes); final imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return null; return null;
} }
return readBarcode( return zxingReadBarcode(
image.getBytes(format: imglib.Format.luminance), image.getBytes(format: imglib.Format.luminance),
width: image.width, width: image.width,
height: image.height, height: image.height,
format: format, params: params,
cropWidth: cropWidth,
cropHeight: cropHeight,
tryHarder: tryHarder,
tryRotate: tryRotate,
); );
} }
/// Reads barcode from image url /// Reads barcode from image url
Future<CodeResult?> readBarcodeImageUrl( Future<Code?> zxingReadBarcodeImageUrl(
String url, { String url, {
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) async { }) async {
final Uint8List imageBytes = final Uint8List imageBytes =
(await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List(); (await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List();
@ -59,36 +39,30 @@ Future<CodeResult?> readBarcodeImageUrl(
if (image == null) { if (image == null) {
return null; return null;
} }
return readBarcode( return zxingReadBarcode(
image.getBytes(format: imglib.Format.luminance), image.getBytes(format: imglib.Format.luminance),
width: image.width, width: image.width,
height: image.height, height: image.height,
format: format, params: params,
cropWidth: cropWidth,
cropHeight: cropHeight,
tryHarder: tryHarder,
tryRotate: tryRotate,
); );
} }
// Reads barcode from Uint8List image bytes // Reads barcode from Uint8List image bytes
CodeResult readBarcode( Code zxingReadBarcode(
Uint8List bytes, { Uint8List bytes, {
required int width, required int width,
required int height, required int height,
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) => }) =>
bindings.readBarcode( bindings
bytes.allocatePointer(), .readBarcode(
format, bytes.allocatePointer(),
width, params?.format ?? Format.any,
height, width,
cropWidth, height,
cropHeight, params?.cropWidth ?? 0,
tryHarder ? 1 : 0, params?.cropHeight ?? 0,
tryRotate ? 1 : 0, params?.tryHarder ?? false ? 1 : 0,
); params?.tryRotate ?? true ? 1 : 0,
)
.toCode();

74
lib/src/logic/barcodes_reader.dart

@ -1,100 +1,72 @@
part of 'zxing.dart'; part of 'zxing.dart';
/// Reads barcodes from String image path /// Reads barcodes from String image path
Future<List<CodeResult>> readBarcodesImagePathString( Future<List<Code>> zxingReadBarcodesImagePathString(
String path, { String path, {
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) => }) =>
readBarcodesImagePath( zxingReadBarcodesImagePath(
XFile(path), XFile(path),
format: format, params: params,
cropWidth: cropWidth,
cropHeight: cropHeight,
tryHarder: tryHarder,
tryRotate: tryRotate,
); );
/// Reads barcodes from XFile image path /// Reads barcodes from XFile image path
Future<List<CodeResult>> readBarcodesImagePath( Future<List<Code>> zxingReadBarcodesImagePath(
XFile path, { XFile path, {
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) async { }) async {
final Uint8List imageBytes = await path.readAsBytes(); final Uint8List imageBytes = await path.readAsBytes();
final imglib.Image? image = imglib.decodeImage(imageBytes); final imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return <CodeResult>[]; return <Code>[];
} }
return readBarcodes( return zxingReadBarcodes(
image.getBytes(format: imglib.Format.luminance), image.getBytes(format: imglib.Format.luminance),
width: image.width, width: image.width,
height: image.height, height: image.height,
format: format, params: params,
cropWidth: cropWidth,
cropHeight: cropHeight,
tryHarder: tryHarder,
tryRotate: tryRotate,
); );
} }
/// Reads barcodes from image url /// Reads barcodes from image url
Future<List<CodeResult>> readBarcodesImageUrl( Future<List<Code>> zxingReadBarcodesImageUrl(
String url, { String url, {
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) async { }) async {
final Uint8List imageBytes = final Uint8List imageBytes =
(await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List(); (await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List();
final imglib.Image? image = imglib.decodeImage(imageBytes); final imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return <CodeResult>[]; return <Code>[];
} }
return readBarcodes( return zxingReadBarcodes(
image.getBytes(format: imglib.Format.luminance), image.getBytes(format: imglib.Format.luminance),
width: image.width, width: image.width,
height: image.height, height: image.height,
format: format, params: params,
cropWidth: cropWidth,
cropHeight: cropHeight,
tryHarder: tryHarder,
tryRotate: tryRotate,
); );
} }
/// Reads barcodes from Uint8List image bytes /// Reads barcodes from Uint8List image bytes
List<CodeResult> readBarcodes( List<Code> zxingReadBarcodes(
Uint8List bytes, { Uint8List bytes, {
required int width, required int width,
required int height, required int height,
int format = Format.Any, Params? params,
int cropWidth = 0,
int cropHeight = 0,
bool tryHarder = false,
bool tryRotate = true,
}) { }) {
final CodeResults result = bindings.readBarcodes( final CodeResults result = bindings.readBarcodes(
bytes.allocatePointer(), bytes.allocatePointer(),
format, params?.format ?? Format.any,
width, width,
height, height,
cropWidth, params?.cropWidth ?? 0,
cropHeight, params?.cropHeight ?? 0,
tryHarder ? 1 : 0, params?.tryHarder ?? false ? 1 : 0,
tryRotate ? 1 : 0, params?.tryRotate ?? true ? 1 : 0,
); );
final List<CodeResult> results = <CodeResult>[]; final List<Code> results = <Code>[];
for (int i = 0; i < result.count; i++) { for (int i = 0; i < result.count; i++) {
results.add(result.results.elementAt(i).ref); results.add(result.results.elementAt(i).ref.toCode());
} }
return results; return results;
} }

18
lib/src/logic/camera_stream.dart

@ -3,29 +3,25 @@ part of 'zxing.dart';
IsolateUtils? isolateUtils; IsolateUtils? isolateUtils;
/// Starts reading barcode from the camera /// Starts reading barcode from the camera
Future<void> startCameraProcessing() async { Future<void> zxingStartCameraProcessing() async {
isolateUtils = IsolateUtils(); isolateUtils = IsolateUtils();
await isolateUtils?.startReadingBarcode(); await isolateUtils?.startReadingBarcode();
} }
/// Stops reading barcode from the camera /// Stops reading barcode from the camera
void stopCameraProcessing() => isolateUtils?.stopReadingBarcode(); void zxingStopCameraProcessing() => isolateUtils?.stopReadingBarcode();
Future<CodeResult> processCameraImage( Future<Code> zxingProcessCameraImage(
CameraImage image, { CameraImage image, {
int format = Format.Any, Params? params,
double cropPercent = 0.5,
bool tryHarder = false,
bool tryInverted = false,
}) async { }) async {
final IsolateData isolateData = final IsolateData isolateData = IsolateData(image, params ?? Params());
IsolateData(image, format, cropPercent, tryHarder, tryInverted); final Code result = await _inference(isolateData);
final CodeResult result = await _inference(isolateData);
return result; return result;
} }
/// Runs inference in another isolate /// Runs inference in another isolate
Future<CodeResult> _inference(IsolateData isolateData) async { Future<Code> _inference(IsolateData isolateData) async {
final ReceivePort responsePort = ReceivePort(); final ReceivePort responsePort = ReceivePort();
isolateUtils?.sendPort isolateUtils?.sendPort
?.send(isolateData..responsePort = responsePort.sendPort); ?.send(isolateData..responsePort = responsePort.sendPort);

13
lib/src/logic/zxing.dart

@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
import 'package:image/image.dart' as imglib; import 'package:image/image.dart' as imglib;
import '../../generated_bindings.dart'; import '../../generated_bindings.dart';
import '../models/models.dart';
import '../utils/extentions.dart'; import '../utils/extentions.dart';
import '../utils/isolate_utils.dart'; import '../utils/isolate_utils.dart';
@ -27,4 +28,14 @@ void setZxingLogEnabled(bool enabled) =>
bindings.setLogEnabled(enabled ? 1 : 0); bindings.setLogEnabled(enabled ? 1 : 0);
/// Returns a readable barcode format name /// Returns a readable barcode format name
String barcodeFormatName(int format) => formatNames[format] ?? 'Unknown'; String zxingBarcodeFormatName(int format) => formatNames[format] ?? 'Unknown';
extension Uint8ListBlobConversion on Uint8List {
/// Allocates a pointer filled with the Uint8List data.
Pointer<Char> allocatePointer() {
final Pointer<Int8> blob = calloc<Int8>(length);
final Int8List blobBytes = blob.asTypedList(length);
blobBytes.setAll(0, this);
return blob.cast<Char>();
}
}

20
lib/src/models/code.dart

@ -0,0 +1,20 @@
import 'dart:typed_data';
import 'position.dart';
// Represents a barcode code
class Code {
Code(
this.isValid,
this.text,
this.rawBytes,
this.format,
this.position,
);
bool isValid; // Whether the code is valid
String? text; // The text of the code
Uint8List? rawBytes; // The raw bytes of the code
int? format; // The format of the code
Position? position; // The position of the code
}

20
lib/src/models/encode.dart

@ -0,0 +1,20 @@
import 'dart:typed_data';
// Encapsulates the result of encoding a barcode.
class Encode {
Encode(
this.isValid,
this.format,
this.text,
this.data,
this.length,
this.error,
);
bool isValid; // Whether the code is valid
int? format; // The format of the code
String? text; // The text of the code
Uint32List? data; // The raw bytes of the code
int? length; // The length of the raw bytes
String? error; // The error message
}

82
lib/src/models/format.dart

@ -0,0 +1,82 @@
// Format Enumerates barcode formats known to this package.
abstract class Format {
// Used as a return value if no valid barcode has been detected
static const int none = 0;
static const int aztec = 1 << 0; // Aztec (2D)
static const int codabar = 1 << 1; // Codabar (1D)
static const int code39 = 1 << 2; // Code39 (1D)
static const int code93 = 1 << 3; // Code93 (1D)
static const int code128 = 1 << 4; // Code128 (1D)
static const int dataBar = 1 << 5; // GS1 DataBar
static const int dataBarExpanded = 1 << 6; // GS1 DataBar Expanded
static const int dataMatrix = 1 << 7; // DataMatrix (2D)
static const int ean8 = 1 << 8; // EAN-8 (1D)
static const int ean13 = 1 << 9; // EAN-13 (1D)
static const int itf = 1 << 10; // ITF (Interleaved Two of Five) (1D)
static const int maxiCode = 1 << 11; // MaxiCode (2D)
static const int pdf417 = 1 << 12; // PDF417 (1D) or (2D)
static const int qrCode = 1 << 13; // QR Code (2D)
static const int upca = 1 << 14; // UPC-A (1D)
static const int upce = 1 << 15; // UPC-E (1D)
static const int oneDCodes = codabar |
code39 |
code93 |
code128 |
ean8 |
ean13 |
itf |
dataBar |
dataBarExpanded |
upca |
upce;
static const int twoDCodes = aztec | dataMatrix | maxiCode | pdf417 | qrCode;
static const int any = oneDCodes | twoDCodes;
}
extension CodeFormat on Format {
String get name => formatNames[this] ?? 'Unknown';
static final List<int> writerFormats = <int>[
Format.qrCode,
Format.dataMatrix,
Format.aztec,
Format.pdf417,
Format.codabar,
Format.code39,
Format.code93,
Format.code128,
Format.ean8,
Format.ean13,
Format.itf,
Format.upca,
Format.upce,
// Format.dataBar,
// Format.dataBarExpanded,
// Format.maxiCode,
];
}
final Map<int, String> formatNames = <int, String>{
Format.none: 'None',
Format.aztec: 'Aztec',
Format.codabar: 'CodaBar',
Format.code39: 'Code39',
Format.code93: 'Code93',
Format.code128: 'Code128',
Format.dataBar: 'DataBar',
Format.dataBarExpanded: 'DataBarExpanded',
Format.dataMatrix: 'DataMatrix',
Format.ean8: 'EAN8',
Format.ean13: 'EAN13',
Format.itf: 'ITF',
Format.maxiCode: 'MaxiCode',
Format.pdf417: 'PDF417',
Format.qrCode: 'QR Code',
Format.upca: 'UPCA',
Format.upce: 'UPCE',
Format.oneDCodes: 'OneD',
Format.twoDCodes: 'TwoD',
Format.any: 'Any',
};

1
lib/src/utils/messages.dart → lib/src/models/messages.dart

@ -1,3 +1,4 @@
// Contains the messages used in the app
class Messages { class Messages {
const Messages({ const Messages({
this.createButton = 'Create', this.createButton = 'Create',

6
lib/src/models/models.dart

@ -0,0 +1,6 @@
export 'code.dart';
export 'encode.dart';
export 'format.dart';
export 'messages.dart';
export 'params.dart';
export 'position.dart';

22
lib/src/models/params.dart

@ -0,0 +1,22 @@
import 'dart:core';
import 'format.dart';
// Represents the parameters for decoding a barcode
class Params {
Params({
this.format = Format.any,
this.cropWidth = 0,
this.cropHeight = 0,
this.tryHarder = false,
this.tryRotate = true,
this.tryInverted = false,
});
int format;
int cropWidth;
int cropHeight;
bool tryHarder;
bool tryRotate;
bool tryInverted;
}

22
lib/src/models/position.dart

@ -0,0 +1,22 @@
/// Represents the position of a barcode in an image.
class Position {
Position(
this.topLeftX,
this.topLeftY,
this.topRightX,
this.topRightY,
this.bottomLeftX,
this.bottomLeftY,
this.bottomRightX,
this.bottomRightY,
);
int topLeftX; // x coordinate of top left corner of barcode
int topLeftY; // y coordinate of top left corner of barcode
int topRightX; // x coordinate of top right corner of barcode
int topRightY; // y coordinate of top right corner of barcode
int bottomLeftX; // x coordinate of bottom left corner of barcode
int bottomLeftY; // y coordinate of bottom left corner of barcode
int bottomRightX; // x coordinate of bottom right corner of barcode
int bottomRightY; // y coordinate of bottom right corner of barcode
}

29
lib/src/ui/dynamic_scanner_overlay.dart

@ -1,18 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../flutter_zxing.dart'; import 'scanner_overlay.dart';
class DynamicScannerOverlay extends ScannerOverlay { class DynamicScannerOverlay extends ScannerOverlay {
const DynamicScannerOverlay(
const DynamicScannerOverlay({ {super.borderColor,
super.borderColor, super.borderWidth,
super.borderWidth, super.overlayColor,
super.overlayColor, super.borderRadius,
super.borderRadius, super.borderLength,
super.borderLength, this.cutOutSize = 0.5})
this.cutOutSize = 0.5 : assert(cutOutSize >= 0 && cutOutSize <= 1,
}) : assert(cutOutSize >= 0 && cutOutSize <= 1, 'The cut out size must be between 0 and 1');
'The cut out size must be between 0 and 1');
@override @override
final double cutOutSize; final double cutOutSize;
@ -55,7 +54,7 @@ class DynamicScannerOverlay extends ScannerOverlay {
rect, rect,
backgroundPaint, backgroundPaint,
) )
// Draw top right corner // Draw top right corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.right - newBorderLength, cutOutRect.right - newBorderLength,
@ -66,7 +65,7 @@ class DynamicScannerOverlay extends ScannerOverlay {
), ),
borderPaint, borderPaint,
) )
// Draw top left corner // Draw top left corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.left, cutOutRect.left,
@ -77,7 +76,7 @@ class DynamicScannerOverlay extends ScannerOverlay {
), ),
borderPaint, borderPaint,
) )
// Draw bottom right corner // Draw bottom right corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.right - newBorderLength, cutOutRect.right - newBorderLength,
@ -88,7 +87,7 @@ class DynamicScannerOverlay extends ScannerOverlay {
), ),
borderPaint, borderPaint,
) )
// Draw bottom left corner // Draw bottom left corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.left, cutOutRect.left,

38
lib/src/ui/fixed_scanner_overlay.dart

@ -1,23 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../flutter_zxing.dart'; import 'scanner_overlay.dart';
class FixedScannerOverlay extends ScannerOverlay { class FixedScannerOverlay extends ScannerOverlay {
const FixedScannerOverlay(
const FixedScannerOverlay({ {super.borderColor,
super.borderColor, super.borderWidth,
super.borderWidth, super.overlayColor,
super.overlayColor, super.borderRadius,
super.borderRadius, super.borderLength,
super.borderLength, this.cutOutSize = 250})
this.cutOutSize = 250 : assert(borderLength <= cutOutSize / 2 + borderWidth * 2,
}) : assert(borderLength <= cutOutSize / 2 + borderWidth * 2, "Border can't be larger than ${cutOutSize / 2 + borderWidth * 2}");
"Border can't be larger than ${cutOutSize / 2 + borderWidth * 2}");
@override @override
final double cutOutSize; final double cutOutSize;
@override @override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
final double width = rect.width; final double width = rect.width;
@ -25,11 +23,11 @@ class FixedScannerOverlay extends ScannerOverlay {
final double height = rect.height; final double height = rect.height;
final double borderOffset = borderWidth / 2; final double borderOffset = borderWidth / 2;
final double newBorderLength = final double newBorderLength =
borderLength > cutOutSize / 2 + borderWidth * 2 borderLength > cutOutSize / 2 + borderWidth * 2
? borderWidthSize / 2 ? borderWidthSize / 2
: borderLength; : borderLength;
final double newCutOutSize = final double newCutOutSize =
cutOutSize < width ? cutOutSize : width - borderOffset; cutOutSize < width ? cutOutSize : width - borderOffset;
final Paint backgroundPaint = Paint() final Paint backgroundPaint = Paint()
..color = overlayColor ..color = overlayColor
@ -61,7 +59,7 @@ class FixedScannerOverlay extends ScannerOverlay {
rect, rect,
backgroundPaint, backgroundPaint,
) )
// Draw top right corner // Draw top right corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.right - newBorderLength, cutOutRect.right - newBorderLength,
@ -72,7 +70,7 @@ class FixedScannerOverlay extends ScannerOverlay {
), ),
borderPaint, borderPaint,
) )
// Draw top left corner // Draw top left corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.left, cutOutRect.left,
@ -83,7 +81,7 @@ class FixedScannerOverlay extends ScannerOverlay {
), ),
borderPaint, borderPaint,
) )
// Draw bottom right corner // Draw bottom right corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.right - newBorderLength, cutOutRect.right - newBorderLength,
@ -94,7 +92,7 @@ class FixedScannerOverlay extends ScannerOverlay {
), ),
borderPaint, borderPaint,
) )
// Draw bottom left corner // Draw bottom left corner
..drawRRect( ..drawRRect(
RRect.fromLTRBAndCorners( RRect.fromLTRBAndCorners(
cutOutRect.left, cutOutRect.left,

30
lib/src/ui/reader_widget.dart

@ -7,18 +7,14 @@ import 'package:flutter/material.dart';
// import 'package:flutter_beep/flutter_beep.dart'; // import 'package:flutter_beep/flutter_beep.dart';
import '../../generated_bindings.dart'; import '../../flutter_zxing.dart';
import '../logic/zxing.dart';
import '../utils/extentions.dart';
import 'fixed_scanner_overlay.dart';
import 'scanner_overlay.dart';
class ReaderWidget extends StatefulWidget { class ReaderWidget extends StatefulWidget {
const ReaderWidget({ const ReaderWidget({
super.key, super.key,
required this.onScan, required this.onScan,
this.onControllerCreated, this.onControllerCreated,
this.codeFormat = Format.Any, this.codeFormat = Format.any,
this.tryHarder = false, this.tryHarder = false,
this.tryInverted = false, this.tryInverted = false,
this.showCroppingRect = true, this.showCroppingRect = true,
@ -33,7 +29,7 @@ class ReaderWidget extends StatefulWidget {
const DecoratedBox(decoration: BoxDecoration(color: Colors.black)), const DecoratedBox(decoration: BoxDecoration(color: Colors.black)),
}); });
final Function(CodeResult) onScan; final Function(Code) onScan;
final Function(CameraController?)? onControllerCreated; final Function(CameraController?)? onControllerCreated;
final int codeFormat; final int codeFormat;
final bool tryHarder; final bool tryHarder;
@ -77,7 +73,7 @@ class _ReaderWidgetState extends State<ReaderWidget>
Future<void> initStateAsync() async { Future<void> initStateAsync() async {
// Spawn a new isolate // Spawn a new isolate
await startCameraProcessing(); await zx.startCameraProcessing();
availableCameras().then((List<CameraDescription> cameras) { availableCameras().then((List<CameraDescription> cameras) {
setState(() { setState(() {
@ -116,7 +112,7 @@ class _ReaderWidgetState extends State<ReaderWidget>
@override @override
void dispose() { void dispose() {
stopCameraProcessing(); zx.stopCameraProcessing();
controller?.removeListener(rebuildOnMount); controller?.removeListener(rebuildOnMount);
controller?.dispose(); controller?.dispose();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
@ -168,14 +164,22 @@ class _ReaderWidgetState extends State<ReaderWidget>
if (!_isProcessing) { if (!_isProcessing) {
_isProcessing = true; _isProcessing = true;
try { try {
final CodeResult result = await processCameraImage( final double cropPercent =
image, widget.showCroppingRect ? widget.cropPercent : 0;
final int cropSize =
(min(image.width, image.height) * cropPercent).round();
final Params params = Params(
format: widget.codeFormat, format: widget.codeFormat,
cropPercent: widget.showCroppingRect ? widget.cropPercent : 0, cropWidth: cropSize,
cropHeight: cropSize,
tryHarder: widget.tryHarder, tryHarder: widget.tryHarder,
tryInverted: widget.tryInverted, tryInverted: widget.tryInverted,
); );
if (result.isValidBool) { final Code result = await zx.processCameraImage(
image,
params: params,
);
if (result.isValid) {
widget.onScan(result); widget.onScan(result);
setState(() {}); setState(() {});
await Future<void>.delayed(widget.scanDelaySuccess); await Future<void>.delayed(widget.scanDelaySuccess);

5
lib/src/ui/ui.dart

@ -0,0 +1,5 @@
export 'dynamic_scanner_overlay.dart';
export 'fixed_scanner_overlay.dart';
export 'reader_widget.dart';
export 'scanner_overlay.dart';
export 'writer_widget.dart';

18
lib/src/ui/writer_widget.dart

@ -9,7 +9,7 @@ class WriterWidget extends StatefulWidget {
const WriterWidget({ const WriterWidget({
super.key, super.key,
this.text = '', this.text = '',
this.format = Format.QRCode, this.format = Format.qrCode,
this.width = 120, this.width = 120,
this.height = 120, this.height = 120,
this.margin = 0, this.margin = 0,
@ -26,7 +26,7 @@ class WriterWidget extends StatefulWidget {
final int margin; final int margin;
final int eccLevel; final int eccLevel;
final Messages messages; final Messages messages;
final Function(EncodeResult result, Uint8List? bytes)? onSuccess; final Function(Encode result, Uint8List? bytes)? onSuccess;
final Function(String error)? onError; final Function(String error)? onError;
@override @override
@ -46,7 +46,7 @@ class _WriterWidgetState extends State<WriterWidget>
final int _maxTextLength = 2000; final int _maxTextLength = 2000;
final List<int> _supportedFormats = CodeFormat.writerFormats; final List<int> _supportedFormats = CodeFormat.writerFormats;
int _codeFormat = Format.QRCode; int _codeFormat = Format.qrCode;
@override @override
void initState() { void initState() {
@ -112,12 +112,12 @@ class _WriterWidgetState extends State<WriterWidget>
items: _supportedFormats items: _supportedFormats
.map((int format) => DropdownMenuItem<int>( .map((int format) => DropdownMenuItem<int>(
value: format, value: format,
child: Text(barcodeFormatName(format)), child: Text(zx.barcodeFormatName(format)),
)) ))
.toList(), .toList(),
onChanged: (int? format) { onChanged: (int? format) {
setState(() { setState(() {
_codeFormat = format ?? Format.QRCode; _codeFormat = format ?? Format.qrCode;
}); });
}, },
), ),
@ -220,7 +220,7 @@ class _WriterWidgetState extends State<WriterWidget>
final int height = int.parse(_heightController.value.text); final int height = int.parse(_heightController.value.text);
final int margin = int.parse(_marginController.value.text); final int margin = int.parse(_marginController.value.text);
final int ecc = int.parse(_eccController.value.text); final int ecc = int.parse(_eccController.value.text);
final EncodeResult result = encodeBarcode( final Encode result = zx.encodeBarcode(
text, text,
format: _codeFormat, format: _codeFormat,
width: width, width: width,
@ -229,12 +229,12 @@ class _WriterWidgetState extends State<WriterWidget>
eccLevel: ecc, eccLevel: ecc,
); );
String? error; String? error;
if (result.isValidBool) { if (result.isValid && result.data != null) {
try { try {
final imglib.Image img = imglib.Image.fromBytes( final imglib.Image img = imglib.Image.fromBytes(
width, width,
height, height,
result.bytes, result.data!,
); );
final Uint8List encodedBytes = Uint8List.fromList( final Uint8List encodedBytes = Uint8List.fromList(
imglib.encodeJpg(img), imglib.encodeJpg(img),
@ -244,7 +244,7 @@ class _WriterWidgetState extends State<WriterWidget>
error = e.toString(); error = e.toString();
} }
} else { } else {
error = result.errorMessage; error = result.error;
} }
if (error != null) { if (error != null) {
widget.onError?.call(error); widget.onError?.call(error);

100
lib/src/utils/extentions.dart

@ -3,80 +3,42 @@ import 'dart:typed_data';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import '../../flutter_zxing.dart'; import '../../zxing_mobile.dart';
extension Uint8ListBlobConversion on Uint8List {
/// Allocates a pointer filled with the Uint8List data.
Pointer<Char> allocatePointer() {
final Pointer<Int8> blob = calloc<Int8>(length);
final Int8List blobBytes = blob.asTypedList(length);
blobBytes.setAll(0, this);
return blob.cast<Char>();
}
}
extension CodeExt on CodeResult { extension CodeExt on CodeResult {
bool get isValidBool => isValid == 1; Code toCode() => Code(
String? get textString => isValid == 1,
text == nullptr ? null : text.cast<Utf8>().toDartString(); text == nullptr ? null : text.cast<Utf8>().toDartString(),
Uint8List get rawBytes => bytes == nullptr
Uint8List.fromList(bytes.cast<Int8>().asTypedList(length)); ? null
String get formatString => barcodeFormatName(format); : Uint8List.fromList(bytes.cast<Int8>().asTypedList(length)),
Pos get position => pos.ref; format,
pos == nullptr ? null : pos.ref.toPosition(),
);
} }
extension EncodeExt on EncodeResult { extension EncodeExt on EncodeResult {
bool get isValidBool => isValid == 1; Encode toEncode() => Encode(
String? get textString => isValid == 1,
text == nullptr ? null : text.cast<Utf8>().toDartString(); format,
String get formatString => barcodeFormatName(format); text == nullptr ? null : text.cast<Utf8>().toDartString(),
Uint32List get bytes => data == nullptr
Uint32List.fromList(data.cast<Int8>().asTypedList(length)); ? null
String get errorMessage => error.cast<Utf8>().toDartString(); : Uint32List.fromList(data.cast<Int8>().asTypedList(length)),
length,
error == nullptr ? null : error.cast<Utf8>().toDartString(),
);
} }
extension CodeFormat on Format { extension PoeExt on Pos {
String get name => formatNames[this] ?? 'Unknown'; Position toPosition() => Position(
topLeftX,
static final List<int> writerFormats = <int>[ topLeftY,
Format.QRCode, topRightX,
Format.DataMatrix, topRightY,
Format.Aztec, bottomLeftX,
Format.PDF417, bottomLeftY,
Format.Codabar, bottomRightX,
Format.Code39, bottomRightY,
Format.Code93, );
Format.Code128,
Format.EAN8,
Format.EAN13,
Format.ITF,
Format.UPCA,
Format.UPCE,
// Format.DataBar,
// Format.DataBarExpanded,
// Format.MaxiCode,
];
} }
final Map<int, String> formatNames = <int, String>{
Format.None: 'None',
Format.Aztec: 'Aztec',
Format.Codabar: 'CodaBar',
Format.Code39: 'Code39',
Format.Code93: 'Code93',
Format.Code128: 'Code128',
Format.DataBar: 'DataBar',
Format.DataBarExpanded: 'DataBarExpanded',
Format.DataMatrix: 'DataMatrix',
Format.EAN8: 'EAN8',
Format.EAN13: 'EAN13',
Format.ITF: 'ITF',
Format.MaxiCode: 'MaxiCode',
Format.PDF417: 'PDF417',
Format.QRCode: 'QR Code',
Format.UPCA: 'UPCA',
Format.UPCE: 'UPCE',
Format.OneDCodes: 'OneD',
Format.TwoDCodes: 'TwoD',
Format.Any: 'Any',
};

34
lib/src/utils/isolate_utils.dart

@ -1,11 +1,10 @@
import 'dart:isolate'; import 'dart:isolate';
import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import '../../generated_bindings.dart';
import '../logic/zxing.dart'; import '../logic/zxing.dart';
import '../models/models.dart';
import 'image_converter.dart'; import 'image_converter.dart';
// Inspired from https://github.com/am15h/object_detection_flutter // Inspired from https://github.com/am15h/object_detection_flutter
@ -14,16 +13,10 @@ import 'image_converter.dart';
class IsolateData { class IsolateData {
IsolateData( IsolateData(
this.cameraImage, this.cameraImage,
this.format, this.params,
this.cropPercent,
this.tryHarder,
this.tryInverted,
); );
CameraImage cameraImage; CameraImage cameraImage;
int format; Params params;
double cropPercent;
bool tryHarder;
bool tryInverted;
SendPort? responsePort; SendPort? responsePort;
} }
@ -63,34 +56,23 @@ class IsolateUtils {
if (isolateData != null) { if (isolateData != null) {
try { try {
final CameraImage image = isolateData.cameraImage; final CameraImage image = isolateData.cameraImage;
final double cropPercent = isolateData.cropPercent;
final Uint8List bytes = await convertImage(image); final Uint8List bytes = await convertImage(image);
final int cropSize =
(min(image.width, image.height) * cropPercent).round();
final bool tryHarder = isolateData.tryHarder;
final bool tryInverted = isolateData.tryInverted;
CodeResult result = readBarcode( Code result = zxingReadBarcode(
bytes, bytes,
width: image.width, width: image.width,
height: image.height, height: image.height,
format: isolateData.format, params: isolateData.params,
cropWidth: cropSize,
cropHeight: cropSize,
tryHarder: tryHarder,
); );
if (result.isValid == 0 && tryInverted) { if (!result.isValid && isolateData.params.tryInverted) {
// try to invert the image and read again // try to invert the image and read again
final Uint8List invertedBytes = invertImage(bytes); final Uint8List invertedBytes = invertImage(bytes);
result = readBarcode( result = zxingReadBarcode(
invertedBytes, invertedBytes,
width: image.width, width: image.width,
height: image.height, height: image.height,
format: isolateData.format, params: isolateData.params,
cropWidth: cropSize,
cropHeight: cropSize,
tryHarder: tryHarder,
); );
} }

4
lib/zxing_cross.dart

@ -0,0 +1,4 @@
import 'flutter_zxing.dart';
Zxing getZxing() => throw UnsupportedError(
'Cannot create an instance of FlutterZxing on the current platform.');

149
lib/zxing_mobile.dart

@ -0,0 +1,149 @@
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'flutter_zxing.dart';
import 'src/logic/zxing.dart';
export 'generated_bindings.dart';
export 'src/logic/zxing.dart';
export 'src/models/models.dart';
export 'src/utils/extentions.dart';
export 'src/utils/image_converter.dart';
Zxing getZxing() => ZxingMobile();
class ZxingMobile implements Zxing {
ZxingMobile();
@override
String version() => zxingVersion();
@override
void setLogEnabled(bool enabled) => setZxingLogEnabled(enabled);
@override
String barcodeFormatName(int format) => zxingBarcodeFormatName(format);
@override
Encode encodeBarcode(
String contents, {
int format = Format.qrCode,
int width = 300,
int height = 300,
int margin = 0,
int eccLevel = 0,
}) =>
zxingEncodeBarcode(
contents,
format: format,
width: width,
height: height,
margin: margin,
eccLevel: eccLevel,
);
@override
Future<void> startCameraProcessing() => zxingStartCameraProcessing();
@override
void stopCameraProcessing() => zxingStopCameraProcessing();
@override
Future<Code> processCameraImage(
CameraImage image, {
Params? params,
}) =>
zxingProcessCameraImage(
image,
params: params,
);
@override
Future<Code?> readBarcodeImagePathString(
String path, {
Params? params,
}) =>
zxingReadBarcodeImagePathString(
path,
params: params,
);
@override
Future<Code?> readBarcodeImagePath(
XFile path, {
Params? params,
}) =>
zxingReadBarcodeImagePath(
path,
params: params,
);
@override
Future<Code?> readBarcodeImageUrl(
String url, {
Params? params,
}) =>
zxingReadBarcodeImageUrl(
url,
params: params,
);
@override
Code readBarcode(
Uint8List bytes, {
required int width,
required int height,
Params? params,
}) =>
zxingReadBarcode(
bytes,
width: width,
height: height,
params: params,
);
@override
Future<List<Code>> readBarcodesImagePathString(
String path, {
Params? params,
}) =>
zxingReadBarcodesImagePathString(
path,
params: params,
);
@override
Future<List<Code>> readBarcodesImagePath(
XFile path, {
Params? params,
}) =>
zxingReadBarcodesImagePath(
path,
params: params,
);
@override
Future<List<Code>> readBarcodesImageUrl(
String url, {
Params? params,
}) =>
zxingReadBarcodesImageUrl(
url,
params: params,
);
@override
List<Code> readBarcodes(
Uint8List bytes, {
required int width,
required int height,
Params? params,
}) =>
zxingReadBarcodes(
bytes,
width: width,
height: height,
params: params,
);
}

104
lib/zxing_web.dart

@ -0,0 +1,104 @@
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'flutter_zxing.dart';
Zxing getZxing() => ZxingWeb();
class ZxingWeb implements Zxing {
ZxingWeb();
@override
String version() => 'Unsupported';
@override
void setLogEnabled(bool enabled) {}
@override
String barcodeFormatName(int format) => 'Unsupported';
@override
Encode encodeBarcode(
String contents, {
int format = Format.qrCode,
int width = 300,
int height = 300,
int margin = 0,
int eccLevel = 0,
}) =>
throw UnimplementedError();
@override
Future<void> startCameraProcessing() => throw UnimplementedError();
@override
void stopCameraProcessing() => throw UnimplementedError();
@override
Future<Code> processCameraImage(
CameraImage image, {
Params? params,
}) =>
throw UnimplementedError();
@override
Future<Code?> readBarcodeImagePathString(
String path, {
Params? params,
}) =>
throw UnimplementedError();
@override
Future<Code?> readBarcodeImagePath(
XFile path, {
Params? params,
}) =>
throw UnimplementedError();
@override
Future<Code?> readBarcodeImageUrl(
String url, {
Params? params,
}) =>
throw UnimplementedError();
@override
Code readBarcode(
Uint8List bytes, {
required int width,
required int height,
Params? params,
}) =>
throw UnimplementedError();
@override
Future<List<Code>> readBarcodesImagePathString(
String path, {
Params? params,
}) =>
throw UnimplementedError();
@override
Future<List<Code>> readBarcodesImagePath(
XFile path, {
Params? params,
}) =>
throw UnimplementedError();
@override
Future<List<Code>> readBarcodesImageUrl(
String url, {
Params? params,
}) =>
throw UnimplementedError();
@override
List<Code> readBarcodes(
Uint8List bytes, {
required int width,
required int height,
Params? params,
}) =>
throw UnimplementedError();
}

2
zxscanner/pubspec.lock

@ -321,7 +321,7 @@ packages:
name: flutter_zxing name: flutter_zxing
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.8.4" version: "0.8.5"
font_awesome_flutter: font_awesome_flutter:
dependency: "direct main" dependency: "direct main"
description: description:

Loading…
Cancel
Save