/* * Copyright (C) 2022 Yubico. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import 'package:flutter/material.dart'; import 'qr_scanner_scan_status.dart'; import 'qr_scanner_util.dart'; /// Return the rounded rect which represents the scanner area for the background /// overlay and the stroke RRect _getScannerAreaRRect(Size size) { double scannerAreaWidth = getScannerAreaWidth(size); var scannerAreaRect = Rect.fromCenter( center: Offset(size.width / 2, size.height / 2), width: scannerAreaWidth, height: scannerAreaWidth); return RRect.fromRectAndRadius( scannerAreaRect, const Radius.circular(scannerAreaRadius)); } // CustomPainter which strokes the scannerArea class _ScannerAreaStrokePainter extends CustomPainter { final Color _strokeColor; _ScannerAreaStrokePainter(this._strokeColor) : super(); @override void paint(Canvas canvas, Size size) { Paint paint = Paint() ..color = _strokeColor ..style = PaintingStyle.stroke ..strokeWidth = 3.0; Path path = Path()..addRRect(_getScannerAreaRRect(size)); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } /// clips the scanner area rounded rect of specific Size class _ScannerAreaClipper extends CustomClipper { @override Path getClip(Size size) { return Path() ..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) ..addRRect(_getScannerAreaRRect(size)) ..fillType = PathFillType.evenOdd; } @override bool shouldReclip(covariant CustomClipper oldClipper) => true; } class QRScannerOverlay extends StatelessWidget { final ScanStatus status; final Size screenSize; const QRScannerOverlay({ super.key, required this.status, required this.screenSize, }); @override Widget build(BuildContext context) { var size = screenSize; return Stack(children: [ /// clip scanner area "hole" into a darkened background ClipPath( clipper: _ScannerAreaClipper(), child: Opacity( opacity: 0.6, child: ColoredBox( color: Colors.black, child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: const [Spacer()], )))), /// draw a stroke around the scanner area Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ CustomPaint( painter: _ScannerAreaStrokePainter(status == ScanStatus.error ? Colors.red.shade400 : Colors.green.shade400), ), ], ), /// extra icon when successful scan occurred if (status == ScanStatus.success) Positioned.fromRect( rect: Rect.fromCenter( center: Offset(size.width / 2, size.height / 2), width: size.width, height: size.height), child: Icon( Icons.check_circle, size: 200, color: Colors.green.shade400, )), ]); } }