diff --git a/example/lib/main.dart b/example/lib/main.dart index 49d97d3..b1c8bc5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_code_scanner/qr_scanner_overlay_shape.dart'; void main() => runApp(MaterialApp(home: QRViewExample())); @@ -33,6 +34,13 @@ class _QRViewExampleState extends State { child: QRView( key: qrKey, onQRViewCreated: _onQRViewCreated, + overlay: QrScannerOverlayShape( + borderColor: Colors.red, + borderRadius: 10, + borderLength: 30, + borderWidth: 10, + cutOutSize: 300, + ), ), flex: 4, ), diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart index 60e08e3..036e27f 100644 --- a/lib/qr_code_scanner.dart +++ b/lib/qr_code_scanner.dart @@ -10,12 +10,15 @@ class QRView extends StatefulWidget { const QRView({ @required Key key, @required this.onQRViewCreated, + this.overlay, }) : assert(key != null), assert(onQRViewCreated != null), super(key: key); final QRViewCreatedCallback onQRViewCreated; + final ShapeBorder overlay; + @override State createState() => _QRViewState(); } @@ -23,26 +26,42 @@ class QRView extends StatefulWidget { class _QRViewState extends State { @override Widget build(BuildContext context) { - var androidView = AndroidView( - viewType: 'net.touchcapture.qr.flutterqr/qrview', - onPlatformViewCreated: _onPlatformViewCreated, + return Stack( + children: [ + _getPlatformQrView(), + widget.overlay != null + ? Container( + decoration: ShapeDecoration( + shape: widget.overlay, + ), + ) + : Container(), + ], ); + } - if (defaultTargetPlatform == TargetPlatform.android) { - return androidView; - } - - if (defaultTargetPlatform == TargetPlatform.iOS) { - return UiKitView( - viewType: 'net.touchcapture.qr.flutterqr/qrview', - onPlatformViewCreated: _onPlatformViewCreated, - creationParams: _CreationParams.fromWidget(0, 0).toMap(), - creationParamsCodec: StandardMessageCodec(), - ); + Widget _getPlatformQrView() { + Widget _platformQrView; + switch (defaultTargetPlatform) { + case TargetPlatform.android: + _platformQrView = AndroidView( + viewType: 'net.touchcapture.qr.flutterqr/qrview', + onPlatformViewCreated: _onPlatformViewCreated, + ); + break; + case TargetPlatform.iOS: + _platformQrView = UiKitView( + viewType: 'net.touchcapture.qr.flutterqr/qrview', + onPlatformViewCreated: _onPlatformViewCreated, + creationParams: _CreationParams.fromWidget(0, 0).toMap(), + creationParamsCodec: StandardMessageCodec(), + ); + break; + default: + throw UnsupportedError( + "Trying to use the default webview implementation for $defaultTargetPlatform but there isn't a default one"); } - - return Text( - '$defaultTargetPlatform is not yet supported by the text_view plugin'); + return _platformQrView; } void _onPlatformViewCreated(int id) { diff --git a/lib/qr_scanner_overlay_shape.dart b/lib/qr_scanner_overlay_shape.dart new file mode 100644 index 0000000..db42605 --- /dev/null +++ b/lib/qr_scanner_overlay_shape.dart @@ -0,0 +1,168 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class QrScannerOverlayShape extends ShapeBorder { + final Color borderColor; + final double borderWidth; + final Color overlayColor; + final double borderRadius; + final double borderLength; + final double cutOutSize; + + QrScannerOverlayShape({ + this.borderColor = Colors.red, + this.borderWidth = 3.0, + this.overlayColor = const Color.fromRGBO(0, 0, 0, 80), + this.borderRadius = 0, + this.borderLength = 40, + this.cutOutSize = 250, + }) : assert( + cutOutSize != null + ? cutOutSize != null + ? borderLength <= cutOutSize / 2 + borderWidth * 2 + : true + : true, + "Border can't be larger than ${cutOutSize / 2 + borderWidth * 2}"); + + @override + EdgeInsetsGeometry get dimensions => const EdgeInsets.all(10.0); + + @override + Path getInnerPath(Rect rect, {TextDirection textDirection}) { + return Path() + ..fillType = PathFillType.evenOdd + ..addPath(getOuterPath(rect), Offset.zero); + } + + @override + Path getOuterPath(Rect rect, {TextDirection textDirection}) { + Path _getLeftTopPath(Rect rect) { + return Path() + ..moveTo(rect.left, rect.bottom) + ..lineTo(rect.left, rect.top) + ..lineTo(rect.right, rect.top); + } + + return _getLeftTopPath(rect) + ..lineTo( + rect.right, + rect.bottom, + ) + ..lineTo( + rect.left, + rect.bottom, + ) + ..lineTo( + rect.left, + rect.top, + ); + } + + @override + void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) { + final width = rect.width; + final borderWidthSize = width / 2; + final height = rect.height; + final borderOffset = borderWidth / 2; + final _borderLength = borderLength > cutOutSize / 2 + borderWidth * 2 + ? borderWidthSize / 2 + : borderLength; + final _cutOutSize = cutOutSize != null && cutOutSize < width + ? cutOutSize + : width - borderOffset; + + final backgroundPaint = Paint() + ..color = overlayColor + ..style = PaintingStyle.fill; + + final borderPaint = Paint() + ..color = borderColor + ..style = PaintingStyle.stroke + ..strokeWidth = borderWidth; + + final boxPaint = Paint() + ..color = borderColor + ..style = PaintingStyle.fill + ..blendMode = BlendMode.dstOut; + + final cutOutRect = Rect.fromLTWH( + width / 2 - _cutOutSize / 2 + borderOffset, + height / 2 - _cutOutSize / 2 + borderOffset, + _cutOutSize - borderOffset * 2, + _cutOutSize - borderOffset * 2, + ); + + canvas.saveLayer( + rect, + backgroundPaint, + ); + + canvas + ..drawRect( + rect, + backgroundPaint, + ) + // Draw top right corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.right - _borderLength, + cutOutRect.top, + cutOutRect.right, + cutOutRect.top + _borderLength, + topRight: Radius.circular(borderRadius), + ), + borderPaint, + ) + // Draw top left corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.left, + cutOutRect.top, + cutOutRect.left + _borderLength, + cutOutRect.top + _borderLength, + topLeft: Radius.circular(borderRadius), + ), + borderPaint, + ) + // Draw bottom right corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.right - _borderLength, + cutOutRect.bottom - _borderLength, + cutOutRect.right, + cutOutRect.bottom, + bottomRight: Radius.circular(borderRadius), + ), + borderPaint, + ) + // Draw bottom left corner + ..drawRRect( + RRect.fromLTRBAndCorners( + cutOutRect.left, + cutOutRect.bottom - _borderLength, + cutOutRect.left + _borderLength, + cutOutRect.bottom, + bottomLeft: Radius.circular(borderRadius), + ), + borderPaint, + ) + ..drawRRect( + RRect.fromRectAndRadius( + cutOutRect, + Radius.circular(borderRadius), + ), + boxPaint, + ) + ..restore(); + } + + @override + ShapeBorder scale(double t) { + return QrScannerOverlayShape( + borderColor: borderColor, + borderWidth: borderWidth, + overlayColor: overlayColor, + ); + } +}