lottis_birthday_escaperoom_app/lib/mobile_scanner_overlay.dart
2024-08-17 05:19:32 +02:00

181 lines
5.3 KiB
Dart

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:lottis_birthday_escaperoom_app/scanned_barcode_label.dart';
import 'package:lottis_birthday_escaperoom_app/scanner_button_widgets.dart';
import 'package:lottis_birthday_escaperoom_app/scanner_error_widget.dart';
class BarcodeScannerWithOverlay extends StatefulWidget {
final MobileScannerController? controller;
const BarcodeScannerWithOverlay({Key? key, this.controller})
: super(key: key);
@override
_BarcodeScannerWithOverlayState createState() =>
_BarcodeScannerWithOverlayState();
}
class _BarcodeScannerWithOverlayState extends State<BarcodeScannerWithOverlay> {
late MobileScannerController _controller;
final AudioPlayer _audioPlayer = AudioPlayer();
Future<void> _playBeepSound() async {
await _audioPlayer.play(AssetSource('sounds/beep.mp3'));
}
@override
void initState() {
super.initState();
_controller = widget.controller ??
MobileScannerController(
formats: const [BarcodeFormat.ean13],
detectionSpeed: DetectionSpeed.noDuplicates);
}
@override
Widget build(BuildContext context) {
final scanWindow = Rect.fromCenter(
center: MediaQuery.sizeOf(context).center(Offset.zero),
width: 300,
height: 40,
);
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text('Scanner with Overlay Example app'),
),
body: Stack(
fit: StackFit.expand,
children: [
Center(
child: MobileScanner(
fit: BoxFit.contain,
controller: _controller,
scanWindow: scanWindow,
errorBuilder: (context, error, child) {
return ScannerErrorWidget(error: error);
},
overlayBuilder: (context, constraints) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.bottomCenter,
child: ScannedBarcodeLabel(barcodes: _controller.barcodes),
),
);
},
onDetect: (barcodes) {
if (barcodes.barcodes.isNotEmpty) {
print("hi");
_playBeepSound();
}
},
),
),
ValueListenableBuilder(
valueListenable: _controller,
builder: (context, value, child) {
if (!value.isInitialized ||
!value.isRunning ||
value.error != null) {
return const SizedBox();
}
return CustomPaint(
painter: ScannerOverlay(scanWindow: scanWindow),
);
},
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ToggleFlashlightButton(controller: _controller),
SwitchCameraButton(controller: _controller),
],
),
),
),
],
),
);
}
@override
Future<void> dispose() async {
super.dispose();
if (widget.controller == null) {
await _controller.dispose();
}
}
}
class ScannerOverlay extends CustomPainter {
const ScannerOverlay({
required this.scanWindow,
this.borderRadius = 12.0,
});
final Rect scanWindow;
final double borderRadius;
@override
void paint(Canvas canvas, Size size) {
// TODO: use `Offset.zero & size` instead of Rect.largest
// we need to pass the size to the custom paint widget
final backgroundPath = Path()..addRect(Rect.largest);
final cutoutPath = Path()
..addRRect(
RRect.fromRectAndCorners(
scanWindow,
topLeft: Radius.circular(borderRadius),
topRight: Radius.circular(borderRadius),
bottomLeft: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
),
);
final backgroundPaint = Paint()
..color = Colors.black.withOpacity(0.5)
..style = PaintingStyle.fill
..blendMode = BlendMode.dstOut;
final backgroundWithCutout = Path.combine(
PathOperation.difference,
backgroundPath,
cutoutPath,
);
final borderPaint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 4.0;
final borderRect = RRect.fromRectAndCorners(
scanWindow,
topLeft: Radius.circular(borderRadius),
topRight: Radius.circular(borderRadius),
bottomLeft: Radius.circular(borderRadius),
bottomRight: Radius.circular(borderRadius),
);
// First, draw the background,
// with a cutout area that is a bit larger than the scan window.
// Finally, draw the scan window itself.
canvas.drawPath(backgroundWithCutout, backgroundPaint);
canvas.drawRRect(borderRect, borderPaint);
}
@override
bool shouldRepaint(ScannerOverlay oldDelegate) {
return scanWindow != oldDelegate.scanWindow ||
borderRadius != oldDelegate.borderRadius;
}
}