2022-10-04 13:12:54 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2022-08-05 11:40:36 +03:00
|
|
|
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<Path> {
|
|
|
|
@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<Path> 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,
|
|
|
|
)),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|