refactor: Projektstruktur in app/ und backend/ aufgeteilt

This commit is contained in:
2026-03-24 15:06:44 +01:00
parent 5f5d5995c5
commit 2673ef658d
449 changed files with 28551 additions and 167 deletions

View File

@@ -0,0 +1,227 @@
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import '../l10n/app_localizations.dart';
import '../models/tasks/barcode_task.dart';
import '../widgets/offline_banner.dart';
class BarcodeCaptureScreen extends StatefulWidget {
final BarcodeTask task;
final Function(List<String>) onBarcodesCompleted;
const BarcodeCaptureScreen({super.key, required this.task, required this.onBarcodesCompleted});
@override
State<BarcodeCaptureScreen> createState() => _BarcodeCaptureScreenState();
}
class _BarcodeCaptureScreenState extends State<BarcodeCaptureScreen> {
final List<String> _scannedBarcodes = [];
final List<TextEditingController> _textControllers = [];
MobileScannerController? _scannerController;
bool _isMobilePlatform = false;
bool _isScannerInitialized = false;
@override
void initState() {
super.initState();
_detectPlatformAndInit();
}
@override
void dispose() {
_scannerController?.dispose();
for (final controller in _textControllers) {
controller.dispose();
}
super.dispose();
}
void _detectPlatformAndInit() {
// Determine if we're on a mobile platform
if (kIsWeb) {
_isMobilePlatform = false;
_initializeDesktopMode();
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.iOS:
_isMobilePlatform = true;
_initializeMobileScanner();
break;
case TargetPlatform.macOS:
case TargetPlatform.windows:
case TargetPlatform.linux:
_isMobilePlatform = false;
_initializeDesktopMode();
break;
default:
_isMobilePlatform = false;
_initializeDesktopMode();
}
}
}
void _initializeMobileScanner() {
try {
_scannerController = MobileScannerController();
setState(() {
_isScannerInitialized = true;
});
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('${AppLocalizations.of(context).cameraError}: $e')));
}
}
}
void _initializeDesktopMode() {
// Create text controllers for desktop input fields
for (int i = 0; i < widget.task.maxBarcodeCount; i++) {
_textControllers.add(TextEditingController());
}
setState(() {
_isScannerInitialized = true;
});
}
void _onBarcodeDetected(BarcodeCapture capture) {
final List<Barcode> barcodes = capture.barcodes;
for (final barcode in barcodes) {
final String? code = barcode.rawValue;
if (code != null && code.isNotEmpty && !_scannedBarcodes.contains(code)) {
if (_scannedBarcodes.length < widget.task.maxBarcodeCount) {
setState(() {
_scannedBarcodes.add(code);
});
}
}
}
}
void _removeBarcode(int index) {
setState(() {
_scannedBarcodes.removeAt(index);
});
}
void _finishTask() {
final List<String> barcodes;
if (_isMobilePlatform) {
barcodes = _scannedBarcodes;
} else {
// Collect barcodes from text fields
barcodes = [];
for (final controller in _textControllers) {
if (controller.text.trim().isNotEmpty) {
barcodes.add(controller.text.trim());
}
}
}
// Navigate back to task view first
Navigator.of(context).pop();
// Then call the completion callback
widget.onBarcodesCompleted(barcodes);
}
bool _canFinish() {
if (_isMobilePlatform) {
return _scannedBarcodes.length >= widget.task.minBarcodeCount;
} else {
int filledFields = 0;
for (final controller in _textControllers) {
if (controller.text.trim().isNotEmpty) {
filledFields++;
}
}
return filledFields >= widget.task.minBarcodeCount;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text(AppLocalizations.of(context).barcodeScan), backgroundColor: Colors.deepPurple[100], leading: IconButton(icon: const Icon(Icons.arrow_back), onPressed: () => Navigator.of(context).pop())), body: Column(children: [OfflineBanner(), Expanded(child: _isScannerInitialized ? (_isMobilePlatform ? _buildMobileView() : _buildDesktopView()) : const Center(child: CircularProgressIndicator()))]));
}
Widget _buildMobileView() {
return Column(
children: [
// Scanner view
Expanded(
flex: 3,
child: Stack(
children: [
MobileScanner(controller: _scannerController, onDetect: _onBarcodeDetected),
// Overlay with scanning frame
Container(decoration: BoxDecoration(color: Colors.black.withValues(alpha: 0.5)), child: Center(child: Container(width: 250, height: 250, decoration: BoxDecoration(border: Border.all(color: Colors.white, width: 2), borderRadius: BorderRadius.circular(12)), child: Container(margin: const EdgeInsets.all(20), decoration: BoxDecoration(border: Border.all(color: Colors.green, width: 2), borderRadius: BorderRadius.circular(8)))))),
],
),
),
// Scanned barcodes list
Expanded(
flex: 2,
child: Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${AppLocalizations.of(context).scannedBarcodes} (${_scannedBarcodes.length}/${widget.task.maxBarcodeCount})', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text('${AppLocalizations.of(context).minBarcodes} ${widget.task.minBarcodeCount} ${AppLocalizations.of(context).barcodesRequired}', style: TextStyle(fontSize: 14, color: Colors.grey[600])),
const SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: _scannedBarcodes.length,
itemBuilder: (context, index) {
return Card(child: ListTile(leading: const Icon(Icons.qr_code), title: Text(_scannedBarcodes[index]), trailing: IconButton(icon: const Icon(Icons.delete, color: Colors.red), onPressed: () => _removeBarcode(index))));
},
),
),
const SizedBox(height: 16),
SizedBox(width: double.infinity, child: ElevatedButton(onPressed: _canFinish() ? _finishTask : null, style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16)), child: Text(AppLocalizations.of(context).finish))),
],
),
),
),
],
);
}
Widget _buildDesktopView() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(AppLocalizations.of(context).enterBarcode, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text('${AppLocalizations.of(context).barcodeEnterDescription} (${widget.task.minBarcodeCount}-${widget.task.maxBarcodeCount})', style: TextStyle(fontSize: 16, color: Colors.grey[600])),
const SizedBox(height: 24),
Expanded(
child: ListView.builder(
itemCount: widget.task.maxBarcodeCount,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TextField(
controller: _textControllers[index],
decoration: InputDecoration(labelText: index < widget.task.minBarcodeCount ? AppLocalizations.of(context).barcodeNumberRequired(index + 1) : AppLocalizations.of(context).barcodeNumberOptional(index + 1), border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.qr_code)),
onChanged: (value) {
setState(() {
// Trigger rebuild to update button state
});
},
),
);
},
),
),
const SizedBox(height: 16),
SizedBox(width: double.infinity, child: ElevatedButton(onPressed: _canFinish() ? _finishTask : null, style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 16)), child: Text(AppLocalizations.of(context).finish))),
],
),
);
}
}