Browse Source

allow to change multi scan mode,

allow to change camera
pull/82/head
Khoren Markosyan 2 years ago
parent
commit
3ef3e587c9
  1. 23
      example/lib/main.dart
  2. 31
      example/lib/widgets/debug_info_widget.dart
  3. 43
      example/lib/widgets/scan_from_gallery_widget.dart
  4. 46
      example/pubspec.lock
  5. 3
      example/pubspec.yaml
  6. 6
      lib/flutter_zxing.dart
  7. 10
      lib/src/logic/barcode_reader.dart
  8. 6
      lib/src/logic/barcodes_reader.dart
  9. 20
      lib/src/models/code.dart
  10. 139
      lib/src/ui/reader_widget.dart
  11. 32
      lib/src/ui/scan_mode_dropdown.dart
  12. 18
      lib/src/utils/extentions.dart
  13. 6
      lib/zxing_mobile.dart
  14. 6
      lib/zxing_web.dart
  15. 1
      pubspec.yaml
  16. 4
      src/native_zxing.cpp
  17. 2
      src/zxing
  18. 408
      zxscanner/pubspec.lock

23
example/lib/main.dart

@ -3,8 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart'; import 'package:flutter_zxing/flutter_zxing.dart';
import 'widgets/debug_info_widget.dart'; import 'widgets/debug_info_widget.dart';
import 'widgets/scan_from_gallery_widget.dart';
import 'widgets/scan_mode_dropdown.dart';
import 'widgets/scan_result_widget.dart'; import 'widgets/scan_result_widget.dart';
import 'widgets/unsupported_platform_widget.dart'; import 'widgets/unsupported_platform_widget.dart';
@ -86,24 +84,13 @@ class _DemoPageState extends State<DemoPage> {
onScanFailure: _onScanFailure, onScanFailure: _onScanFailure,
onMultiScan: _onMultiScanSuccess, onMultiScan: _onMultiScanSuccess,
onMultiScanFailure: _onMultiScanFailure, onMultiScanFailure: _onMultiScanFailure,
onMultiScanModeChanged: _onMultiScanModeChanged,
isMultiScan: isMultiScan, isMultiScan: isMultiScan,
scanDelay: isMultiScan scanDelay: isMultiScan
? Duration.zero ? Duration.zero
: const Duration(milliseconds: 500), : const Duration(milliseconds: 500),
tryInverted: true, tryInverted: true,
), actionButtonsAlignment: Alignment.bottomLeft,
ScanFromGalleryWidget(
onScan: _onScanSuccess,
onScanFailure: _onScanFailure,
),
// Change single/multi scan mode dropdown button
ScanModeDropdown(
isMultiScan: isMultiScan,
onChanged: (value) {
setState(() {
isMultiScan = value;
});
},
), ),
if (showDebugInfo) if (showDebugInfo)
DebugInfoWidget( DebugInfoWidget(
@ -192,4 +179,10 @@ class _DemoPageState extends State<DemoPage> {
failedScans = 0; failedScans = 0;
}); });
} }
_onMultiScanModeChanged(bool isMultiScan) {
setState(() {
this.isMultiScan = isMultiScan;
});
}
} }

31
example/lib/widgets/debug_info_widget.dart

@ -19,22 +19,37 @@ class DebugInfoWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
TextStyle? style =
Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.white);
return Align( return Align(
alignment: Alignment.topLeft, alignment: Alignment.topCenter,
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(10.0),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
child: Container( child: Container(
color: Colors.white.withOpacity(0.7), color: Colors.black54,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
child: Column( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Flexible(
'Success: $successScans\nFailed: $failedScans\nDuration: $duration ms', child: SingleChildScrollView(
style: Theme.of(context).textTheme.bodySmall, scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Success: $successScans', style: style),
const SizedBox(width: 10),
Text('Failed: $failedScans', style: style),
const SizedBox(width: 10),
Text('Duration: $duration ms', style: style),
],
),
),
), ),
const SizedBox(width: 10),
TextButton( TextButton(
onPressed: onReset, onPressed: onReset,
child: const Text('Reset'), child: const Text('Reset'),

43
example/lib/widgets/scan_from_gallery_widget.dart

@ -1,43 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:image_picker/image_picker.dart';
class ScanFromGalleryWidget extends StatelessWidget {
const ScanFromGalleryWidget({
Key? key,
this.onScan,
this.onScanFailure,
}) : super(key: key);
final Function(Code)? onScan;
final Function(Code?)? onScanFailure;
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 20,
right: 20,
child: FloatingActionButton(
onPressed: _onFromGalleryButtonTapped,
child: const Icon(Icons.image),
),
);
}
void _onFromGalleryButtonTapped() async {
final XFile? file =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (file != null) {
final Code? result = await zx.readBarcodeImagePath(
file,
params: DecodeParams(tryInverted: true),
);
if (result != null && result.isValid) {
onScan?.call(result);
} else {
result?.error = 'No barcode found';
onScanFailure?.call(result);
}
}
}
}

46
example/pubspec.lock

@ -5,10 +5,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: ed7cc591a948744994714375caf9a2ce89e1d82e8243997c8a2994d57181c212 sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.5" version: "3.3.6"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -29,34 +29,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: camera name: camera
sha256: "045e7739f9362f3c01d5c7187ac7e2ba9e2bc9f2ae6470ecdcc5e34d060ed81c" sha256: e7ac55af24a890d20276821eb3c95857074d03b7de6f9892b99a205ee30bd179
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.10.2+1" version: "0.10.3"
camera_android: camera_android:
dependency: transitive dependency: transitive
description: description:
name: camera_android name: camera_android
sha256: "417e9eddda8025d7342f82ee53b214f149793e95c8490c3440a46144409f7966" sha256: e491c836147f60dd8a54cf3895fd2960e13b21b78a9d15b563a1b6c70894f142
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.10.2+3" version: "0.10.4"
camera_avfoundation: camera_avfoundation:
dependency: transitive dependency: transitive
description: description:
name: camera_avfoundation name: camera_avfoundation
sha256: eeda6a7947b1f7c47395c4459342d1e6866014186449e141a251f0549aba0c8b sha256: "6a68c20593d4cd58974d555f74a48b244f9db28cc9156de57781122d11b8754b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.9.10+2" version: "0.9.11"
camera_platform_interface: camera_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: camera_platform_interface name: camera_platform_interface
sha256: "0eedd642d905ca24f1c483fe9ea0d0e7287b86a402845c28d24df28cc7b0ee6e" sha256: b632be28e61d00a233f67d98ea90fd7041956f27a1c65500188ee459be60e15f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.4" version: "2.4.0"
camera_web: camera_web:
dependency: transitive dependency: transitive
description: description:
@ -101,10 +101,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: f71079978789bc2fe78d79227f1f8cfe195b31bbd8db2399b0d15a4b96fb843b sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+2" version: "0.3.3+4"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -167,14 +167,6 @@ packages:
relative: true relative: true
source: path source: path
version: "1.0.0" version: "1.0.0"
font_awesome_flutter:
dependency: "direct main"
description:
name: font_awesome_flutter
sha256: "875dbb9ec1ad30d68102019ceb682760d06c72747c1c5b7885781b95f88569cc"
url: "https://pub.dev"
source: hosted
version: "10.3.0"
http: http:
dependency: transitive dependency: transitive
description: description:
@ -192,7 +184,7 @@ packages:
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
image: image:
dependency: "direct main" dependency: transitive
description: description:
name: image name: image
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
@ -200,7 +192,7 @@ packages:
source: hosted source: hosted
version: "3.3.0" version: "3.3.0"
image_picker: image_picker:
dependency: "direct main" dependency: transitive
description: description:
name: image_picker name: image_picker
sha256: f98d76672d309c8b7030c323b3394669e122d52b307d2bbd8d06bd70f5b2aabe sha256: f98d76672d309c8b7030c323b3394669e122d52b307d2bbd8d06bd70f5b2aabe
@ -211,10 +203,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: b1cbfec0f5aef427a18eb573f5445af8c9c568626bf3388553e40c263d3f7368 sha256: "385f12ee9c7288575572c7873a332016ec45ebd092e1c2f6bd421b4a9ad21f1d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.5+5" version: "0.8.5+6"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@ -227,10 +219,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: "39c013200046d14c58b71dc4fa3d00e425fc9c699d589136cd3ca018727c0493" sha256: "8ffb14b43713d7c43fb21299cc18181cc5b39bd3ea1cc427a085c6400fe5aa52"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.6+6" version: "0.8.6+7"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -405,5 +397,5 @@ packages:
source: hosted source: hosted
version: "6.2.2" version: "6.2.2"
sdks: sdks:
dart: ">=2.18.0 <4.0.0" dart: ">=2.18.0 <3.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

3
example/pubspec.yaml

@ -11,9 +11,6 @@ dependencies:
sdk: flutter sdk: flutter
flutter_zxing: flutter_zxing:
path: ../ path: ../
font_awesome_flutter: ^10.3.0
image: ^3.3.0
image_picker: ^0.8.6
dev_dependencies: dev_dependencies:
flutter_lints: ^2.0.1 flutter_lints: ^2.0.1

6
lib/flutter_zxing.dart

@ -45,19 +45,19 @@ abstract class Zxing {
}); });
/// Reads barcode from String image path /// Reads barcode from String image path
Future<Code?> readBarcodeImagePathString( Future<Code> readBarcodeImagePathString(
String path, { String path, {
DecodeParams? params, DecodeParams? params,
}); });
/// Reads barcode from XFile image path /// Reads barcode from XFile image path
Future<Code?> readBarcodeImagePath( Future<Code> readBarcodeImagePath(
XFile path, { XFile path, {
DecodeParams? params, DecodeParams? params,
}); });
/// Reads barcode from image url /// Reads barcode from image url
Future<Code?> readBarcodeImageUrl( Future<Code> readBarcodeImageUrl(
String url, { String url, {
DecodeParams? params, DecodeParams? params,
}); });

10
lib/src/logic/barcode_reader.dart

@ -1,7 +1,7 @@
part of 'zxing.dart'; part of 'zxing.dart';
/// Reads barcode from String image path /// Reads barcode from String image path
Future<Code?> zxingReadBarcodeImagePathString( Future<Code> zxingReadBarcodeImagePathString(
String path, { String path, {
DecodeParams? params, DecodeParams? params,
}) => }) =>
@ -11,14 +11,14 @@ Future<Code?> zxingReadBarcodeImagePathString(
); );
/// Reads barcode from XFile image path /// Reads barcode from XFile image path
Future<Code?> zxingReadBarcodeImagePath( Future<Code> zxingReadBarcodeImagePath(
XFile path, { XFile path, {
DecodeParams? params, DecodeParams? params,
}) async { }) async {
final Uint8List imageBytes = await path.readAsBytes(); final Uint8List imageBytes = await path.readAsBytes();
imglib.Image? image = imglib.decodeImage(imageBytes); imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return null; return Code();
} }
image = resizeToMaxSize(image, params?.maxSize); image = resizeToMaxSize(image, params?.maxSize);
return zxingReadBarcode( return zxingReadBarcode(
@ -30,7 +30,7 @@ Future<Code?> zxingReadBarcodeImagePath(
} }
/// Reads barcode from image url /// Reads barcode from image url
Future<Code?> zxingReadBarcodeImageUrl( Future<Code> zxingReadBarcodeImageUrl(
String url, { String url, {
DecodeParams? params, DecodeParams? params,
}) async { }) async {
@ -38,7 +38,7 @@ Future<Code?> zxingReadBarcodeImageUrl(
(await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List(); (await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List();
imglib.Image? image = imglib.decodeImage(imageBytes); imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return null; return Code(error: 'Failed to decode image');
} }
image = resizeToMaxSize(image, params?.maxSize); image = resizeToMaxSize(image, params?.maxSize);
return zxingReadBarcode( return zxingReadBarcode(

6
lib/src/logic/barcodes_reader.dart

@ -18,7 +18,7 @@ Future<Codes> zxingReadBarcodesImagePath(
final Uint8List imageBytes = await path.readAsBytes(); final Uint8List imageBytes = await path.readAsBytes();
imglib.Image? image = imglib.decodeImage(imageBytes); imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return Codes(<Code>[], 0); return Codes();
} }
image = resizeToMaxSize(image, params?.maxSize); image = resizeToMaxSize(image, params?.maxSize);
return zxingReadBarcodes( return zxingReadBarcodes(
@ -38,7 +38,7 @@ Future<Codes> zxingReadBarcodesImageUrl(
(await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List(); (await NetworkAssetBundle(Uri.parse(url)).load(url)).buffer.asUint8List();
imglib.Image? image = imglib.decodeImage(imageBytes); imglib.Image? image = imglib.decodeImage(imageBytes);
if (image == null) { if (image == null) {
return Codes(<Code>[], 0); return Codes();
} }
image = resizeToMaxSize(image, params?.maxSize); image = resizeToMaxSize(image, params?.maxSize);
return zxingReadBarcodes( return zxingReadBarcodes(
@ -80,5 +80,5 @@ Codes _readBarcodes(
for (int i = 0; i < result.count; i++) { for (int i = 0; i < result.count; i++) {
results.add(result.results.elementAt(i).ref.toCode()); results.add(result.results.elementAt(i).ref.toCode());
} }
return Codes(results, result.duration); return Codes(codes: results, duration: result.duration);
} }

20
lib/src/models/code.dart

@ -4,17 +4,17 @@ import 'position.dart';
// Represents a barcode code // Represents a barcode code
class Code { class Code {
Code( Code({
this.text, this.text,
this.isValid, this.isValid = false,
this.error, this.error,
this.rawBytes, this.rawBytes,
this.format, this.format,
this.position, this.position,
this.isInverted, this.isInverted = false,
this.isMirrored, this.isMirrored = false,
this.duration, this.duration = 0,
); });
String? text; // The text of the code String? text; // The text of the code
bool isValid; // Whether the code is valid bool isValid; // Whether the code is valid
@ -29,10 +29,10 @@ class Code {
// Represents a list of barcode codes // Represents a list of barcode codes
class Codes { class Codes {
Codes( Codes({
this.codes, this.codes = const <Code>[],
this.duration, this.duration = 0,
); });
List<Code> codes; // The list of codes List<Code> codes; // The list of codes
int duration; // The duration of the decoding in milliseconds int duration; // The duration of the decoding in milliseconds

139
lib/src/ui/reader_widget.dart

@ -4,8 +4,10 @@ import 'dart:math';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import '../../flutter_zxing.dart'; import '../../flutter_zxing.dart';
import 'scan_mode_dropdown.dart';
/// Widget to scan a code from the camera stream /// Widget to scan a code from the camera stream
class ReaderWidget extends StatefulWidget { class ReaderWidget extends StatefulWidget {
@ -16,13 +18,18 @@ class ReaderWidget extends StatefulWidget {
this.onMultiScan, this.onMultiScan,
this.onMultiScanFailure, this.onMultiScanFailure,
this.onControllerCreated, this.onControllerCreated,
this.onMultiScanModeChanged,
this.isMultiScan = false, this.isMultiScan = false,
this.codeFormat = Format.any, this.codeFormat = Format.any,
this.tryHarder = false, this.tryHarder = false,
this.tryInverted = false, this.tryInverted = false,
this.showScannerOverlay = true, this.showScannerOverlay = true,
this.scannerOverlay, this.scannerOverlay,
this.actionButtonsAlignment = Alignment.topCenter,
this.actionButtonsPadding = const EdgeInsets.all(10),
this.showFlashlight = true, this.showFlashlight = true,
this.showToggleCamera = true,
this.showGallery = true,
this.allowPinchZoom = true, this.allowPinchZoom = true,
this.scanDelay = const Duration(milliseconds: 1000), this.scanDelay = const Duration(milliseconds: 1000),
this.scanDelaySuccess = const Duration(milliseconds: 1000), this.scanDelaySuccess = const Duration(milliseconds: 1000),
@ -47,6 +54,9 @@ class ReaderWidget extends StatefulWidget {
/// Called when the camera controller is created /// Called when the camera controller is created
final Function(CameraController?)? onControllerCreated; final Function(CameraController?)? onControllerCreated;
/// Called when the multi scan mode is changed
final Function(bool)? onMultiScanModeChanged;
/// Allow multiple scans /// Allow multiple scans
final bool isMultiScan; final bool isMultiScan;
@ -65,9 +75,21 @@ class ReaderWidget extends StatefulWidget {
/// Custom scanner overlay /// Custom scanner overlay
final ScannerOverlay? scannerOverlay; final ScannerOverlay? scannerOverlay;
/// Align for action buttons
final AlignmentGeometry actionButtonsAlignment;
/// Padding for action buttons
final EdgeInsetsGeometry actionButtonsPadding;
/// Show flashlight button /// Show flashlight button
final bool showFlashlight; final bool showFlashlight;
/// Show toggle camera
final bool showGallery;
/// Show toggle camera
final bool showToggleCamera;
/// Allow pinch zoom /// Allow pinch zoom
final bool allowPinchZoom; final bool allowPinchZoom;
@ -93,6 +115,7 @@ class ReaderWidget extends StatefulWidget {
class _ReaderWidgetState extends State<ReaderWidget> class _ReaderWidgetState extends State<ReaderWidget>
with TickerProviderStateMixin, WidgetsBindingObserver { with TickerProviderStateMixin, WidgetsBindingObserver {
List<CameraDescription> cameras = <CameraDescription>[]; List<CameraDescription> cameras = <CameraDescription>[];
CameraDescription? selectedCamera;
CameraController? controller; CameraController? controller;
bool _cameraOn = false; bool _cameraOn = false;
@ -106,12 +129,15 @@ class _ReaderWidgetState extends State<ReaderWidget>
// true when code detecting is ongoing // true when code detecting is ongoing
bool _isProcessing = false; bool _isProcessing = false;
Codes results = Codes(<Code>[], 0); bool isMultiScan = false;
Codes results = Codes();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
isMultiScan = widget.isMultiScan;
initStateAsync(); initStateAsync();
} }
@ -123,7 +149,8 @@ class _ReaderWidgetState extends State<ReaderWidget>
setState(() { setState(() {
this.cameras = cameras; this.cameras = cameras;
if (cameras.isNotEmpty) { if (cameras.isNotEmpty) {
onNewCameraSelected(cameras.first); selectedCamera = cameras.first;
onNewCameraSelected(selectedCamera);
} }
}); });
}); });
@ -171,7 +198,10 @@ class _ReaderWidgetState extends State<ReaderWidget>
} }
} }
Future<void> onNewCameraSelected(CameraDescription cameraDescription) async { Future<void> onNewCameraSelected(CameraDescription? cameraDescription) async {
if (cameraDescription == null) {
return;
}
final CameraController? oldController = controller; final CameraController? oldController = controller;
if (oldController != null) { if (oldController != null) {
// controller?.removeListener(rebuildOnMount); // controller?.removeListener(rebuildOnMount);
@ -235,7 +265,7 @@ class _ReaderWidgetState extends State<ReaderWidget>
await Future<void>.delayed(widget.scanDelaySuccess); await Future<void>.delayed(widget.scanDelaySuccess);
} }
} else { } else {
results = Codes(<Code>[], 0); results = Codes();
widget.onMultiScanFailure?.call(result); widget.onMultiScanFailure?.call(result);
} }
} else { } else {
@ -327,12 +357,58 @@ class _ReaderWidgetState extends State<ReaderWidget>
controller?.setZoomLevel(_scaleFactor); controller?.setZoomLevel(_scaleFactor);
}, },
), ),
Align(
alignment: widget.actionButtonsAlignment,
child: Padding(
padding: widget.actionButtonsPadding,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
color: Colors.black.withOpacity(0.5),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.showFlashlight && isCameraReady) if (widget.showFlashlight && isCameraReady)
Positioned( IconButton(
bottom: 20, onPressed: _onFlashButtonTapped,
left: 20, color: Colors.white,
child: FloatingActionButton( icon: Icon(
onPressed: () { _flashIcon(
controller?.value.flashMode ?? FlashMode.off),
),
),
if (widget.showGallery && isCameraReady)
IconButton(
onPressed: _onGalleryButtonTapped,
color: Colors.white,
icon: const Icon(Icons.photo_library),
),
if (widget.showToggleCamera && isCameraReady)
IconButton(
onPressed: _onCameraButtonTapped,
color: Colors.white,
icon: const Icon(Icons.switch_camera),
),
],
),
),
),
),
),
ScanModeDropdown(
isMultiScan: isMultiScan,
onChanged: (bool value) {
setState(() {
isMultiScan = value;
});
widget.onMultiScanModeChanged?.call(value);
},
),
],
);
}
void _onFlashButtonTapped() {
FlashMode mode = controller!.value.flashMode; FlashMode mode = controller!.value.flashMode;
if (mode == FlashMode.torch) { if (mode == FlashMode.torch) {
mode = FlashMode.off; mode = FlashMode.off;
@ -341,31 +417,46 @@ class _ReaderWidgetState extends State<ReaderWidget>
} }
controller?.setFlashMode(mode); controller?.setFlashMode(mode);
setState(() {}); setState(() {});
}, }
backgroundColor: Colors.black26,
child: _FlashIcon( Future<void> _onGalleryButtonTapped() async {
flashMode: controller?.value.flashMode ?? FlashMode.off)), final XFile? file =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (file != null) {
final Code result = await zx.readBarcodeImagePath(
file,
params: DecodeParams(
format: widget.codeFormat,
tryHarder: widget.tryHarder,
tryInverted: widget.tryInverted,
isMultiScan: widget.isMultiScan,
), ),
],
); );
if (result.isValid) {
widget.onScan?.call(result);
} else {
widget.onScanFailure?.call(result);
}
} }
} }
class _FlashIcon extends StatelessWidget { void _onCameraButtonTapped() {
const _FlashIcon({required this.flashMode}); final int cameraIndex = cameras.indexOf(controller!.description);
final FlashMode flashMode; final int nextCameraIndex = (cameraIndex + 1) % cameras.length;
selectedCamera = cameras[nextCameraIndex];
onNewCameraSelected(selectedCamera);
}
@override IconData _flashIcon(FlashMode mode) {
Widget build(BuildContext context) { switch (mode) {
switch (flashMode) {
case FlashMode.torch: case FlashMode.torch:
return const Icon(Icons.flash_on); return Icons.flash_on;
case FlashMode.off: case FlashMode.off:
return const Icon(Icons.flash_off); return Icons.flash_off;
case FlashMode.always: case FlashMode.always:
return const Icon(Icons.flash_on); return Icons.flash_on;
case FlashMode.auto: case FlashMode.auto:
return const Icon(Icons.flash_auto); return Icons.flash_auto;
} }
} }
} }

32
example/lib/widgets/scan_mode_dropdown.dart → lib/src/ui/scan_mode_dropdown.dart

@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
class ScanModeDropdown extends StatelessWidget { class ScanModeDropdown extends StatelessWidget {
const ScanModeDropdown({ const ScanModeDropdown({
Key? key, super.key,
this.isMultiScan = false, this.isMultiScan = false,
this.onChanged, this.onChanged,
}) : super(key: key); });
final bool isMultiScan; final bool isMultiScan;
final Function(bool value)? onChanged; final Function(bool value)? onChanged;
@ -13,30 +13,36 @@ class ScanModeDropdown extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomRight,
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.all(10),
child: Container( child: Container(
height: 56, padding: const EdgeInsets.symmetric(horizontal: 10),
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.black54,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: DropdownButtonHideUnderline( child: DropdownButtonHideUnderline(
child: DropdownButton<bool>( child: DropdownButton<bool>(
value: isMultiScan, value: isMultiScan,
items: const [ dropdownColor: Colors.black87,
DropdownMenuItem( items: const <DropdownMenuItem<bool>>[
DropdownMenuItem<bool>(
value: false, value: false,
child: Text('Single Scan'), child: Text(
'Single Code',
style: TextStyle(color: Colors.white),
), ),
DropdownMenuItem( ),
DropdownMenuItem<bool>(
value: true, value: true,
child: Text('Multi Scan'), child: Text(
'Multi Code',
style: TextStyle(color: Colors.white),
),
), ),
], ],
onChanged: (value) => onChanged?.call(value ?? false), onChanged: (bool? value) => onChanged?.call(value ?? false),
), ),
), ),
), ),

18
lib/src/utils/extentions.dart

@ -8,17 +8,17 @@ import '../../zxing_mobile.dart';
extension CodeExt on CodeResult { extension CodeExt on CodeResult {
Code toCode() { Code toCode() {
return Code( return Code(
text == nullptr ? null : text.cast<Utf8>().toDartString(), text: text == nullptr ? null : text.cast<Utf8>().toDartString(),
isValid == 1, isValid: isValid == 1,
error == nullptr ? null : error.cast<Utf8>().toDartString(), error: error == nullptr ? null : error.cast<Utf8>().toDartString(),
bytes == nullptr rawBytes: bytes == nullptr
? null ? null
: Uint8List.fromList(bytes.cast<Int8>().asTypedList(length)), : Uint8List.fromList(bytes.cast<Int8>().asTypedList(length)),
format, format: format,
pos == nullptr ? null : pos.ref.toPosition(), position: pos == nullptr ? null : pos.ref.toPosition(),
isInverted == 1, isInverted: isInverted == 1,
isMirrored == 1, isMirrored: isMirrored == 1,
duration, duration: duration,
); );
} }
} }

6
lib/zxing_mobile.dart

@ -53,21 +53,21 @@ class ZxingMobile implements Zxing {
await zxingProcessCameraImage(image, params: params) as Codes; await zxingProcessCameraImage(image, params: params) as Codes;
@override @override
Future<Code?> readBarcodeImagePathString( Future<Code> readBarcodeImagePathString(
String path, { String path, {
DecodeParams? params, DecodeParams? params,
}) => }) =>
zxingReadBarcodeImagePathString(path, params: params); zxingReadBarcodeImagePathString(path, params: params);
@override @override
Future<Code?> readBarcodeImagePath( Future<Code> readBarcodeImagePath(
XFile path, { XFile path, {
DecodeParams? params, DecodeParams? params,
}) => }) =>
zxingReadBarcodeImagePath(path, params: params); zxingReadBarcodeImagePath(path, params: params);
@override @override
Future<Code?> readBarcodeImageUrl( Future<Code> readBarcodeImageUrl(
String url, { String url, {
DecodeParams? params, DecodeParams? params,
}) => }) =>

6
lib/zxing_web.dart

@ -46,21 +46,21 @@ class ZxingWeb implements Zxing {
throw UnimplementedError(); throw UnimplementedError();
@override @override
Future<Code?> readBarcodeImagePathString( Future<Code> readBarcodeImagePathString(
String path, { String path, {
DecodeParams? params, DecodeParams? params,
}) => }) =>
throw UnimplementedError(); throw UnimplementedError();
@override @override
Future<Code?> readBarcodeImagePath( Future<Code> readBarcodeImagePath(
XFile path, { XFile path, {
DecodeParams? params, DecodeParams? params,
}) => }) =>
throw UnimplementedError(); throw UnimplementedError();
@override @override
Future<Code?> readBarcodeImageUrl( Future<Code> readBarcodeImageUrl(
String url, { String url, {
DecodeParams? params, DecodeParams? params,
}) => }) =>

1
pubspec.yaml

@ -13,6 +13,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
image: ^3.0.0 image: ^3.0.0
image_picker: ^0.8.0
dev_dependencies: dev_dependencies:
ffigen: ^7.0.0 # dart run ffigen ffigen: ^7.0.0 # dart run ffigen

4
src/native_zxing.cpp

@ -3,6 +3,7 @@
#include "MultiFormatWriter.h" #include "MultiFormatWriter.h"
#include "BitMatrix.h" #include "BitMatrix.h"
#include "native_zxing.h" #include "native_zxing.h"
#include "ZXVersion.h"
#include <locale> #include <locale>
#include <codecvt> #include <codecvt>
@ -22,8 +23,7 @@ extern "C"
FUNCTION_ATTRIBUTE FUNCTION_ATTRIBUTE
char const *version() char const *version()
{ {
// TODO: use ZXING_VERSION_STR after zxing-cpp 2.1 is released return ZXING_VERSION_STR;
return "2.0.0";
} }
FUNCTION_ATTRIBUTE FUNCTION_ATTRIBUTE

2
src/zxing

@ -1 +1 @@
Subproject commit 86b253d699b13b1da8fca0c5e4fbd62691582d5a Subproject commit 72023ad3dded9f3f94b5d85bc10385e0cf76620a

408
zxscanner/pubspec.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save