181 lines
5.3 KiB
Dart
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;
|
||
|
}
|
||
|
}
|