Sander Roest

wip

... ... @@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:mobile_scanner_example/scanned_barcode_label.dart';
import 'package:mobile_scanner_example/scanner_error_widget.dart';
class BarcodeScannerWithScanWindow extends StatefulWidget {
... ...
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobile_scanner_example/barcode_scanner_analyze_image.dart';
import 'package:mobile_scanner_example/barcode_scanner_controller.dart';
import 'package:mobile_scanner_example/barcode_scanner_listview.dart';
... ... @@ -8,8 +9,11 @@ import 'package:mobile_scanner_example/barcode_scanner_simple.dart';
import 'package:mobile_scanner_example/barcode_scanner_window.dart';
import 'package:mobile_scanner_example/barcode_scanner_zoom.dart';
import 'package:mobile_scanner_example/mobile_scanner_overlay.dart';
import 'package:mobile_scanner_example/picklist/picklist_result.dart';
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
runApp(
const MaterialApp(
title: 'Mobile Scanner Example',
... ... @@ -91,6 +95,11 @@ class MyHome extends StatelessWidget {
'Analyze image from file',
const BarcodeScannerAnalyzeImage(),
),
_buildItem(
context,
'Picklist mode',
const PicklistResult(),
),
],
),
),
... ...
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:mobile_scanner_example/picklist/classes/detect_collision.dart';
import 'package:mobile_scanner_example/picklist/widgets/crosshair.dart';
import 'package:mobile_scanner_example/picklist/widgets/draw_detected_barcodes.dart';
import 'package:mobile_scanner_example/scanner_error_widget.dart';
class BarcodeScannerPicklist extends StatefulWidget {
const BarcodeScannerPicklist({super.key});
@override
State<BarcodeScannerPicklist> createState() => _BarcodeScannerPicklistState();
}
class _BarcodeScannerPicklistState extends State<BarcodeScannerPicklist>
with WidgetsBindingObserver {
final _mobileScannerController = MobileScannerController(autoStart: false);
StreamSubscription<Object?>? _barcodesSubscription;
final _scannerDisabled = ValueNotifier(false);
late final Offset _crosshair;
bool barcodeDetected = false;
@override
void didChangeDependencies() {
_crosshair = MediaQuery.sizeOf(context).center(Offset.zero);
super.didChangeDependencies();
}
@override
void initState() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
WidgetsBinding.instance.addObserver(this);
_barcodesSubscription = _mobileScannerController.barcodes.listen(
_handleBarcodes,
);
super.initState();
unawaited(_mobileScannerController.start());
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (!_mobileScannerController.value.isInitialized) {
return;
}
switch (state) {
case AppLifecycleState.detached:
case AppLifecycleState.hidden:
case AppLifecycleState.paused:
return;
case AppLifecycleState.resumed:
_barcodesSubscription =
_mobileScannerController.barcodes.listen(_handleBarcodes);
unawaited(_mobileScannerController.start());
case AppLifecycleState.inactive:
unawaited(_barcodesSubscription?.cancel());
_barcodesSubscription = null;
unawaited(_mobileScannerController.stop());
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
unawaited(_barcodesSubscription?.cancel());
_barcodesSubscription = null;
super.dispose();
_mobileScannerController.dispose();
}
void _handleBarcodes(BarcodeCapture barcodes) {
if (_scannerDisabled.value) {
return;
}
for (final barcode in barcodes.barcodes) {
if (isOffsetInsideShape(
_crosshair,
barcode.corners,
)) {
if (!barcodeDetected) {
barcodeDetected = true;
Navigator.of(context).pop(barcode);
}
return;
}
}
}
@override
Widget build(BuildContext context) {
const boxFit = BoxFit.contain;
return PopScope(
onPopInvokedWithResult: (didPop, result) {
if (didPop) {
SystemChrome.setPreferredOrientations([...DeviceOrientation.values]);
}
},
child: Scaffold(
appBar: AppBar(title: const Text('Picklist scanner')),
backgroundColor: Colors.black,
body: StreamBuilder(
stream: _mobileScannerController.barcodes,
builder: (context, snapshot) {
final barcodes = snapshot.data;
if (barcodes == null) {
debugPrint('ISNULL');
}
return Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (_) => _scannerDisabled.value = true,
onPointerUp: (_) => _scannerDisabled.value = false,
onPointerCancel: (_) => _scannerDisabled.value = false,
child: Stack(
fit: StackFit.expand,
children: [
MobileScanner(
controller: _mobileScannerController,
errorBuilder: (context, error, child) {
return ScannerErrorWidget(error: error);
},
fit: boxFit,
),
...drawDetectedBarcodes(
barcodes: barcodes?.barcodes,
cameraPreviewSize: _mobileScannerController.value.size,
fit: boxFit,
),
ValueListenableBuilder(
valueListenable: _scannerDisabled,
builder: (context, value, child) {
return Crosshair(
scannerDisabled: value,
);
},
),
],
),
);
},
)
// body: Stack(
// fit: StackFit.expand,
// children: [
// MobileScanner(
// controller: _mobileScannerController,
// errorBuilder: (context, error, child) {
// return ScannerErrorWidget(error: error);
// },
// fit: boxFit,
// ),
// ...drawDetectedBarcodes(
// controller: _mobileScannerController,
// cameraPreviewSize: _mobileScannerController.value.size,
// fit: boxFit,
// ),
// ...drawDetectedBarcodes(
// controller: _mobileScannerController,
// cameraPreviewSize: _mobileScannerController.value.size,
// fit: boxFit,
// ),
// // barcodes: _mobileScannerController. value. _barcodes.value,
// // cameraPreviewSize: _mobileScannerController.value.size,
// // fit: boxFit,
// // ),
// CustomPaint(
// painter: BarcodeOverlay(
// barcodeCorners: [
// const Offset(0, 0),
// const Offset(50, 0),
// const Offset(50, 50),
// const Offset(0, 50)
// ],
// barcodeSize: Size(50, 50),
// boxFit: boxFit,
// cameraPreviewSize: _mobileScannerController.value.size,
// ),
// ),
// Crosshair(
// crosshairRectangle: _crosshairRectangle,
// scannerDisabled: _scannerDisabled.value,
// ),
// ],
// ),
,
),
);
}
}
... ...
//Some magic created by chatGPT
import 'package:flutter/material.dart';
bool isOffsetInsideShape(Offset point, List<Offset> shape) {
return _isPointInPolygon(shape, point);
}
bool _isPointInPolygon(List<Offset> polygon, Offset point) {
// Use the ray-casting algorithm for checking if a point is inside a polygon
bool inside = false;
for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
if ((polygon[i].dy > point.dy) != (polygon[j].dy > point.dy) &&
(point.dx <
(polygon[j].dx - polygon[i].dx) *
(point.dy - polygon[i].dy) /
(polygon[j].dy - polygon[i].dy) +
polygon[i].dx)) {
inside = !inside;
}
}
return inside;
}
// import 'package:flutter/material.dart';
//
// bool crosshairFullyFitsIntoShape(Rect rect, List<Offset> shape) {
// final List<Offset> rectCorners = [
// Offset(rect.left, rect.top),
// Offset(rect.right, rect.top),
// Offset(rect.right, rect.bottom),
// Offset(rect.left, rect.bottom),
// ];
//
// // Check if all rect corners are inside the shape
// for (final Offset corner in rectCorners) {
// if (!_isPointInPolygon(shape, corner)) {
// return false; // If any corner is outside, the rectangle doesn't fit fully
// }
// }
//
// return true; // All corners are inside the shape
// }
//
// bool _isPointInPolygon(List<Offset> polygon, Offset point) {
// // Use the ray-casting algorithm for checking if a point is inside a polygon
// bool inside = false;
// for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
// if ((polygon[i].dy > point.dy) != (polygon[j].dy > point.dy) &&
// (point.dx <
// (polygon[j].dx - polygon[i].dx) *
// (point.dy - polygon[i].dy) /
// (polygon[j].dy - polygon[i].dy) +
// polygon[i].dx)) {
// inside = !inside;
// }
// }
// return inside;
// }
// // import 'package:flutter/material.dart';
// //
// // bool crosshairTouchesBarcode(Rect rect, List<Offset> shape) {
// // final List<Offset> rectCorners = [
// // Offset(rect.left, rect.top),
// // Offset(rect.right, rect.top),
// // Offset(rect.right, rect.bottom),
// // Offset(rect.left, rect.bottom),
// // ];
// // final List<Offset> edges = [shape[0], shape[1], shape[2], shape[3], shape[0]];
// //
// // // Check edge intersection
// // for (int i = 0; i < edges.length - 1; i++) {
// // for (int j = 0; j < rectCorners.length; j++) {
// // final int next = (j + 1) % rectCorners.length;
// // if (_checkIntersection(
// // edges[i],
// // edges[i + 1],
// // rectCorners[j],
// // rectCorners[next],
// // )) {
// // return true;
// // }
// // }
// // }
// //
// // // Check if any rect corner is inside the shape
// // for (final Offset corner in rectCorners) {
// // if (_isPointInPolygon(shape, corner)) {
// // return true;
// // }
// // }
// //
// // return false;
// // }
// //
// // bool _checkIntersection(Offset p1, Offset p2, Offset p3, Offset p4) {
// // // Calculate the intersection of two line segments
// // double s1X;
// // double s1Y;
// // double s2X;
// // double s2Y;
// // s1X = p2.dx - p1.dx;
// // s1Y = p2.dy - p1.dy;
// // s2X = p4.dx - p3.dx;
// // s2Y = p4.dy - p3.dy;
// //
// // double s;
// // double t;
// // s = (-s1Y * (p1.dx - p3.dx) + s1X * (p1.dy - p3.dy)) /
// // (-s2X * s1Y + s1X * s2Y);
// // t = (s2X * (p1.dy - p3.dy) - s2Y * (p1.dx - p3.dx)) /
// // (-s2X * s1Y + s1X * s2Y);
// //
// // return s >= 0 && s <= 1 && t >= 0 && t <= 1;
// // }
// //
// // bool _isPointInPolygon(List<Offset> polygon, Offset point) {
// // // Ray-casting algorithm for checking if a point is inside a polygon
// // bool inside = false;
// // for (int i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
// // if ((polygon[i].dy > point.dy) != (polygon[j].dy > point.dy) &&
// // (point.dx <
// // (polygon[j].dx - polygon[i].dx) *
// // (point.dy - polygon[i].dy) /
// // (polygon[j].dy - polygon[i].dy) +
// // polygon[i].dx)) {
// // inside = !inside;
// // }
// // }
// // return inside;
// // }
... ...
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:mobile_scanner_example/picklist/barcode_scanner_picklist.dart';
class PicklistResult extends StatefulWidget {
const PicklistResult({super.key});
@override
State<PicklistResult> createState() => _PicklistResultState();
}
class _PicklistResultState extends State<PicklistResult> {
String barcode = 'Scan Something!';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Picklist mode')),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(barcode),
ElevatedButton(
onPressed: () async {
final scannedBarcode =
await Navigator.of(context).push<Barcode>(
MaterialPageRoute(
builder: (context) => const BarcodeScannerPicklist(),
),
);
setState(
() {
if (scannedBarcode == null) {
barcode = 'Scan Something!';
return;
}
if (scannedBarcode.displayValue == null) {
barcode = '>>binary<<';
return;
}
barcode = scannedBarcode.displayValue!;
},
);
},
child: const Text('Scan'),
),
],
),
),
),
),
);
}
}
... ...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class BarcodeOverlay extends CustomPainter {
BarcodeOverlay({
required this.barcodeCorners,
required this.barcodeSize,
required this.boxFit,
required this.cameraPreviewSize,
});
final List<Offset> barcodeCorners;
final Size barcodeSize;
final BoxFit boxFit;
final Size cameraPreviewSize;
@override
void paint(Canvas canvas, Size size) {
if (barcodeCorners.isEmpty ||
barcodeSize.isEmpty ||
cameraPreviewSize.isEmpty) {
return;
}
final adjustedSize = applyBoxFit(boxFit, cameraPreviewSize, size);
double verticalPadding = size.height - adjustedSize.destination.height;
double horizontalPadding = size.width - adjustedSize.destination.width;
if (verticalPadding > 0) {
verticalPadding = verticalPadding / 2;
} else {
verticalPadding = 0;
}
if (horizontalPadding > 0) {
horizontalPadding = horizontalPadding / 2;
} else {
horizontalPadding = 0;
}
final double ratioWidth;
final double ratioHeight;
if (!kIsWeb && defaultTargetPlatform == TargetPlatform.iOS) {
ratioWidth = barcodeSize.width / adjustedSize.destination.width;
ratioHeight = barcodeSize.height / adjustedSize.destination.height;
} else {
ratioWidth = cameraPreviewSize.width / adjustedSize.destination.width;
ratioHeight = cameraPreviewSize.height / adjustedSize.destination.height;
}
final List<Offset> adjustedOffset = [
for (final offset in barcodeCorners)
Offset(
offset.dx / ratioWidth + horizontalPadding,
offset.dy / ratioHeight + verticalPadding,
),
];
final cutoutPath = Path()..addPolygon(adjustedOffset, true);
final backgroundPaint = Paint()
..color = Colors.red.withOpacity(1.0)
..style = PaintingStyle.fill
..blendMode = BlendMode.dstOut;
canvas.drawPath(cutoutPath, backgroundPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
... ...
import 'package:flutter/material.dart';
class Crosshair extends StatelessWidget {
const Crosshair({
super.key,
required this.scannerDisabled,
});
final bool scannerDisabled;
@override
Widget build(BuildContext context) {
return Center(
child: Icon(
Icons.close,
color: scannerDisabled ? Colors.green : Colors.red,
),
);
}
}
... ...
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:mobile_scanner_example/picklist/widgets/barcode_overlay.dart';
List<Widget> drawDetectedBarcodes({
required List<Barcode>? barcodes,
required Size cameraPreviewSize,
required BoxFit fit,
}) {
final barcodeWidgets = <Widget>[];
if (barcodes == null || barcodes.isEmpty) {
debugPrint('EMPTY!!!');
}
if (barcodes != null) {
for (final barcode in barcodes) {
barcodeWidgets.add(
CustomPaint(
painter: BarcodeOverlay(
barcodeCorners: barcode.corners,
barcodeSize: barcode.size,
boxFit: fit,
cameraPreviewSize: cameraPreviewSize,
),
),
);
debugPrint(
'barcodeCorners => ${barcode.corners.map((e) => 'x: ${e.dx}, y: ${e.dy} ')}, barcodeSize => width: ${barcode.size.width}, height: ${barcode.size.height}, cameraPreviewSize => width: ${cameraPreviewSize.width}, height: ${cameraPreviewSize.height} ',
);
}
debugPrint(barcodeWidgets.length.toString());
}
return barcodeWidgets;
}
... ...