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 { late MobileScannerController _controller; final AudioPlayer _audioPlayer = AudioPlayer(); Future _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 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; } }