From edd9f4cade933b1e1acef9b9a63c00a0cd5a14b5 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Sun, 19 Mar 2023 09:26:16 +0100 Subject: [PATCH] feat: 3783 - brought back the old visor New file: * `smooth_barcode_scanner_visor.dart`: Barcode Scanner Visor widget. Impacted files: * `smooth_barcode_scanner_mlkit.dart`: now using the new `SmoothBarcodeScannerVisor` widget * `smooth_barcode_scanner_zxing.dart`: now using the new `SmoothBarcodeScannerVisor` widget --- .../scan/smooth_barcode_scanner_mlkit.dart | 219 ++++++++---------- .../scan/smooth_barcode_scanner_visor.dart | 122 ++++++++++ .../scan/smooth_barcode_scanner_zxing.dart | 111 ++++----- 3 files changed, 262 insertions(+), 190 deletions(-) create mode 100644 packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_visor.dart diff --git a/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_mlkit.dart b/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_mlkit.dart index 4ab4301269f..0a479104489 100644 --- a/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_mlkit.dart +++ b/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_mlkit.dart @@ -1,14 +1,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart' as zxing; import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/helpers/app_helper.dart'; import 'package:smooth_app/helpers/camera_helper.dart'; import 'package:smooth_app/helpers/haptic_feedback_helper.dart'; import 'package:smooth_app/pages/scan/scan_header.dart'; +import 'package:smooth_app/pages/scan/smooth_barcode_scanner_visor.dart'; import 'package:smooth_app/widgets/screen_visibility.dart'; import 'package:visibility_detector/visibility_detector.dart'; @@ -103,137 +101,106 @@ class _SmoothBarcodeScannerMLKitState extends State _visible = false; await _stop(); }, - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - final Rect scanWindow = Rect.fromCenter( - center: Offset( - constraints.maxWidth / 2, - constraints.maxHeight / 2, - ), - width: constraints.maxWidth - 2 * MINIMUM_TOUCH_SIZE, - height: constraints.maxHeight, - ); - return Stack( - children: [ - MobileScanner( - controller: _controller, - fit: BoxFit.cover, - scanWindow: scanWindow, - errorBuilder: ( - BuildContext context, - MobileScannerException error, - Widget? child, - ) => - EMPTY_WIDGET, - onDetect: (final BarcodeCapture capture) async { - for (final Barcode barcode in capture.barcodes) { - final String? string = barcode.displayValue; - if (string != null) { - await widget.onScan(string); - } - } - }, - ), - Container( - decoration: ShapeDecoration( - shape: zxing.QrScannerOverlayShape( - borderColor: Colors.white, - borderRadius: 10, - borderLength: 30, - borderWidth: 10, - cutOutWidth: - constraints.maxWidth - 2 * MINIMUM_TOUCH_SIZE, - cutOutHeight: constraints.maxHeight, - ), - ), - ), - const Align( - alignment: Alignment.topCenter, - child: ScanHeader(), - ), - Center( - child: SvgPicture.asset( - 'assets/icons/visor_icon.svg', - package: AppHelper.APP_PACKAGE, - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Row( - mainAxisAlignment: _showFlipCameraButton - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (_showFlipCameraButton) - IconButton( - color: Colors.white, - icon: ValueListenableBuilder( - valueListenable: _controller.cameraFacingState, - builder: ( - BuildContext context, - CameraFacing state, - Widget? child, - ) { - switch (state) { - case CameraFacing.front: - return const Icon(Icons.camera_front); - case CameraFacing.back: - return const Icon(Icons.camera_rear); - } - }, - ), - onPressed: () async { - SmoothHapticFeedback.click(); - await _controller.switchCamera(); - }, - ), - ValueListenableBuilder( - valueListenable: _controller.hasTorchState, + child: Stack( + children: [ + MobileScanner( + controller: _controller, + fit: BoxFit.cover, + errorBuilder: ( + BuildContext context, + MobileScannerException error, + Widget? child, + ) => + EMPTY_WIDGET, + onDetect: (final BarcodeCapture capture) async { + for (final Barcode barcode in capture.barcodes) { + final String? string = barcode.displayValue; + if (string != null) { + await widget.onScan(string); + } + } + }, + ), + Center(child: SmoothBarcodeScannerVisor()), + const Align( + alignment: Alignment.topCenter, + child: ScanHeader(), + ), + Align( + alignment: Alignment.bottomCenter, + child: Row( + mainAxisAlignment: _showFlipCameraButton + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (_showFlipCameraButton) + IconButton( + color: Colors.white, + icon: ValueListenableBuilder( + valueListenable: _controller.cameraFacingState, builder: ( BuildContext context, - bool? state, + CameraFacing state, Widget? child, ) { - if (state != true) { - return EMPTY_WIDGET; + switch (state) { + case CameraFacing.front: + return const Icon(Icons.camera_front); + case CameraFacing.back: + return const Icon(Icons.camera_rear); } - return IconButton( - color: Colors.white, - icon: ValueListenableBuilder( - valueListenable: _controller.torchState, - builder: ( - BuildContext context, - TorchState state, - Widget? child, - ) { - switch (state) { - case TorchState.off: - return const Icon( - Icons.flash_off, - color: Colors.white, - ); - case TorchState.on: - return const Icon( - Icons.flash_on, - color: Colors.white, - ); - } - }, - ), - onPressed: () async { - SmoothHapticFeedback.click(); - await _controller.toggleTorch(); - }, - ); }, ), - ], + onPressed: () async { + SmoothHapticFeedback.click(); + await _controller.switchCamera(); + }, + ), + ValueListenableBuilder( + valueListenable: _controller.hasTorchState, + builder: ( + BuildContext context, + bool? state, + Widget? child, + ) { + if (state != true) { + return EMPTY_WIDGET; + } + return IconButton( + color: Colors.white, + icon: ValueListenableBuilder( + valueListenable: _controller.torchState, + builder: ( + BuildContext context, + TorchState state, + Widget? child, + ) { + switch (state) { + case TorchState.off: + return const Icon( + Icons.flash_off, + color: Colors.white, + ); + case TorchState.on: + return const Icon( + Icons.flash_on, + color: Colors.white, + ); + } + }, + ), + onPressed: () async { + SmoothHapticFeedback.click(); + await _controller.toggleTorch(); + }, + ); + }, ), - ), - ], - ); - }, + ], + ), + ), + ], ), ); } diff --git a/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_visor.dart b/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_visor.dart new file mode 100644 index 00000000000..ec8f9c814d4 --- /dev/null +++ b/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_visor.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:smooth_app/helpers/app_helper.dart'; + +/// Barcode Scanner Visor widget. +class SmoothBarcodeScannerVisor extends StatelessWidget { + /// Returns the Size of the visor + static Size _getSize(BuildContext context) => Size( + MediaQuery.of(context).size.width * 0.8, + 150.0, + ); + + @override + Widget build(BuildContext context) => SizedBox.fromSize( + size: _getSize(context), + child: CustomPaint( + painter: _ScanVisorPainter(), + child: Center( + child: SvgPicture.asset( + 'assets/icons/visor_icon.svg', + width: 35.0, + height: 32.0, + package: AppHelper.APP_PACKAGE, + ), + ), + ), + ); +} + +class _ScanVisorPainter extends CustomPainter { + static const double strokeWidth = 3.0; + static const double _fullCornerSize = 31.0; + static const double _halfCornerSize = _fullCornerSize / 2; + static const Radius _borderRadius = Radius.circular(_halfCornerSize); + + final Paint _paint = Paint() + ..strokeWidth = strokeWidth + ..color = Colors.white + ..style = PaintingStyle.stroke; + + @override + void paint(Canvas canvas, Size size) { + final Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height); + canvas.drawPath(getPath(rect, false), _paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; + + /// Returns a path to draw the visor + /// [includeLineBetweenCorners] will draw lines between each corner, instead + /// of moving the cursor + static Path getPath(Rect rect, bool includeLineBetweenCorners) { + final double bottomPosition; + if (includeLineBetweenCorners) { + bottomPosition = rect.bottom - strokeWidth; + } else { + bottomPosition = rect.bottom; + } + + final Path path = Path() + // Top left + ..moveTo(rect.left, rect.top + _fullCornerSize) + ..lineTo(rect.left, rect.top + _halfCornerSize) + ..arcToPoint( + Offset(rect.left + _halfCornerSize, rect.top), + radius: _borderRadius, + ) + ..lineTo(rect.left + _fullCornerSize, rect.top); + + // Top right + if (includeLineBetweenCorners) { + path.lineTo(rect.right - _fullCornerSize, rect.top); + } else { + path.moveTo(rect.right - _fullCornerSize, rect.top); + } + + path + ..lineTo(rect.right - _halfCornerSize, rect.top) + ..arcToPoint( + Offset(rect.right, _halfCornerSize), + radius: _borderRadius, + ) + ..lineTo(rect.right, rect.top + _fullCornerSize); + + // Bottom right + if (includeLineBetweenCorners) { + path.lineTo(rect.right, bottomPosition - _fullCornerSize); + } else { + path.moveTo(rect.right, bottomPosition - _fullCornerSize); + } + + path + ..lineTo(rect.right, bottomPosition - _halfCornerSize) + ..arcToPoint( + Offset(rect.right - _halfCornerSize, bottomPosition), + radius: _borderRadius, + ) + ..lineTo(rect.right - _fullCornerSize, bottomPosition); + + // Bottom left + if (includeLineBetweenCorners) { + path.lineTo(rect.left + _fullCornerSize, bottomPosition); + } else { + path.moveTo(rect.left + _fullCornerSize, bottomPosition); + } + + path + ..lineTo(rect.left + _halfCornerSize, bottomPosition) + ..arcToPoint( + Offset(rect.left, bottomPosition - _halfCornerSize), + radius: _borderRadius, + ) + ..lineTo(rect.left, bottomPosition - _fullCornerSize); + + if (includeLineBetweenCorners) { + path.lineTo(rect.left, rect.top + _halfCornerSize); + } + + return path; + } +} diff --git a/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_zxing.dart b/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_zxing.dart index bb35fa2e399..55c641cabb0 100644 --- a/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_zxing.dart +++ b/packages/smooth_app/lib/pages/scan/smooth_barcode_scanner_zxing.dart @@ -2,13 +2,12 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; -import 'package:smooth_app/helpers/app_helper.dart'; import 'package:smooth_app/helpers/camera_helper.dart'; import 'package:smooth_app/helpers/haptic_feedback_helper.dart'; import 'package:smooth_app/pages/scan/scan_header.dart'; +import 'package:smooth_app/pages/scan/smooth_barcode_scanner_visor.dart'; import 'package:smooth_app/themes/constant_icons.dart'; import 'package:smooth_app/widgets/screen_visibility.dart'; import 'package:visibility_detector/visibility_detector.dart'; @@ -71,74 +70,58 @@ class _SmoothBarcodeScannerZXingState extends State { _visible = false; _controller?.pauseCamera(); }, - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) => Stack( - children: [ - QRView( - key: _qrKey, - onQRViewCreated: _onQRViewCreated, - overlay: QrScannerOverlayShape( - borderColor: Colors.white, - borderRadius: 10, - borderLength: 30, - borderWidth: 10, - cutOutWidth: constraints.maxWidth - 2 * MINIMUM_TOUCH_SIZE, - cutOutHeight: constraints.maxHeight, - ), - formatsAllowed: _barcodeFormats, - ), - const Align( - alignment: Alignment.topCenter, - child: ScanHeader(), - ), - Center( - child: SvgPicture.asset( - 'assets/icons/visor_icon.svg', - package: AppHelper.APP_PACKAGE, - ), - ), - Align( - alignment: Alignment.bottomCenter, - child: Row( - mainAxisAlignment: _showFlipCameraButton - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (_showFlipCameraButton) - IconButton( - icon: Icon(ConstantIcons.instance.getCameraFlip()), + child: Stack( + children: [ + QRView( + key: _qrKey, + onQRViewCreated: _onQRViewCreated, + formatsAllowed: _barcodeFormats, + ), + Center(child: SmoothBarcodeScannerVisor()), + const Align( + alignment: Alignment.topCenter, + child: ScanHeader(), + ), + Align( + alignment: Alignment.bottomCenter, + child: Row( + mainAxisAlignment: _showFlipCameraButton + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (_showFlipCameraButton) + IconButton( + icon: Icon(ConstantIcons.instance.getCameraFlip()), + color: Colors.white, + onPressed: () async { + SmoothHapticFeedback.click(); + await _controller?.flipCamera(); + setState(() {}); + }, + ), + FutureBuilder( + future: _controller?.getFlashStatus(), + builder: (_, final AsyncSnapshot snapshot) { + final bool? flashOn = snapshot.data; + if (flashOn == null) { + return EMPTY_WIDGET; + } + return IconButton( + icon: Icon(flashOn ? Icons.flash_on : Icons.flash_off), color: Colors.white, onPressed: () async { SmoothHapticFeedback.click(); - await _controller?.flipCamera(); + await _controller?.toggleFlash(); setState(() {}); }, - ), - FutureBuilder( - future: _controller?.getFlashStatus(), - builder: (_, final AsyncSnapshot snapshot) { - final bool? flashOn = snapshot.data; - if (flashOn == null) { - return EMPTY_WIDGET; - } - return IconButton( - icon: - Icon(flashOn ? Icons.flash_on : Icons.flash_off), - color: Colors.white, - onPressed: () async { - SmoothHapticFeedback.click(); - await _controller?.toggleFlash(); - setState(() {}); - }, - ); - }, - ), - ], - ), + ); + }, + ), + ], ), - ], - ), + ), + ], ), );