mirror of
https://github.com/DefinedNet/mobile_nebula.git
synced 2025-01-19 03:37:02 +00:00
301dc6c394
These are a few more minor updated dependencies, mostly in go.mod. I also see that the pubspec now has an update to the flutter version, which should have happened previously along with the flutter upgrade, but it didn't for whatever reason.
229 lines
6.7 KiB
Dart
229 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
|
|
|
class ScanQRScreen extends StatefulWidget {
|
|
@override
|
|
State<ScanQRScreen> createState() => _ScanQRScreenState();
|
|
}
|
|
|
|
class _ScanQRScreenState extends State<ScanQRScreen> {
|
|
final MobileScannerController cameraController = MobileScannerController(
|
|
autoStart: true,
|
|
formats: [BarcodeFormat.qrCode],
|
|
);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final scanWindow = Rect.fromCenter(
|
|
center: MediaQuery.sizeOf(context).center(Offset.zero),
|
|
width: 250,
|
|
height: 250,
|
|
);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('Scan QR')),
|
|
backgroundColor: Colors.black,
|
|
body: Stack(fit: StackFit.expand, children: [
|
|
Center(
|
|
child: MobileScanner(
|
|
fit: BoxFit.contain,
|
|
controller: cameraController,
|
|
scanWindow: scanWindow,
|
|
onDetect: (BarcodeCapture barcodes) {
|
|
var barcode = barcodes.barcodes.firstOrNull;
|
|
if (barcode != null && mounted) {
|
|
cameraController.stop().then((_) {
|
|
Navigator.pop(context, barcode.rawValue);
|
|
});
|
|
}
|
|
}),
|
|
),
|
|
ValueListenableBuilder(
|
|
valueListenable: cameraController,
|
|
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: cameraController),
|
|
SwitchCameraButton(controller: cameraController),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
]));
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// we need to pass the size to the custom paint widget
|
|
final backgroundPath = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
|
|
|
|
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.withValues(alpha: 0.5)
|
|
..style = PaintingStyle.fill
|
|
..blendMode = BlendMode.srcOver;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
class SwitchCameraButton extends StatelessWidget {
|
|
const SwitchCameraButton({required this.controller, super.key});
|
|
|
|
final MobileScannerController controller;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ValueListenableBuilder(
|
|
valueListenable: controller,
|
|
builder: (context, state, child) {
|
|
if (!state.isInitialized || !state.isRunning) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
final int? availableCameras = state.availableCameras;
|
|
|
|
if (availableCameras != null && availableCameras < 2) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
final Widget icon;
|
|
|
|
switch (state.cameraDirection) {
|
|
case CameraFacing.front:
|
|
icon = const Icon(Icons.camera_front);
|
|
case CameraFacing.back:
|
|
icon = const Icon(Icons.camera_rear);
|
|
}
|
|
|
|
return IconButton(
|
|
iconSize: 32.0,
|
|
color: Colors.white,
|
|
icon: icon,
|
|
onPressed: () async {
|
|
await controller.switchCamera();
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class ToggleFlashlightButton extends StatelessWidget {
|
|
const ToggleFlashlightButton({required this.controller, super.key});
|
|
|
|
final MobileScannerController controller;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ValueListenableBuilder(
|
|
valueListenable: controller,
|
|
builder: (context, controllerState, child) {
|
|
if (!controllerState.isInitialized || !controllerState.isRunning) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
switch (controllerState.torchState) {
|
|
case TorchState.auto:
|
|
return IconButton(
|
|
color: Colors.white,
|
|
iconSize: 32.0,
|
|
icon: const Icon(Icons.flash_auto),
|
|
onPressed: () async {
|
|
await controller.toggleTorch();
|
|
},
|
|
);
|
|
case TorchState.off:
|
|
return IconButton(
|
|
color: Colors.white,
|
|
iconSize: 32.0,
|
|
icon: const Icon(Icons.flash_off),
|
|
onPressed: () async {
|
|
await controller.toggleTorch();
|
|
},
|
|
);
|
|
case TorchState.on:
|
|
return IconButton(
|
|
color: Colors.white,
|
|
iconSize: 32.0,
|
|
icon: const Icon(Icons.flash_on),
|
|
onPressed: () async {
|
|
await controller.toggleTorch();
|
|
},
|
|
);
|
|
case TorchState.unavailable:
|
|
return const SizedBox.square(
|
|
dimension: 48.0,
|
|
child: Icon(
|
|
Icons.no_flash,
|
|
size: 32.0,
|
|
color: Colors.grey,
|
|
),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
}
|