Fix cap photo flow to always open review dialog with fallback

This commit is contained in:
gary
2026-02-21 22:51:44 +01:00
parent e392b99bef
commit 94eec93e13

View File

@@ -154,10 +154,48 @@ class _MosaicHomePageState extends State<MosaicHomePage> {
imageQuality: 95,
maxWidth: 1800,
);
if (captured == null) return;
if (captured == null) {
debugPrint('[cap-photo] Capture cancelled by user.');
return;
}
Uint8List bytes;
try {
bytes = await captured.readAsBytes();
} catch (e, st) {
debugPrint('[cap-photo] Failed to read captured bytes: $e\n$st');
final fallbackImage = img.Image(width: 1, height: 1)
..setPixelRgb(0, 0, 255, 152, 0);
bytes = Uint8List.fromList(
img.encodePng(fallbackImage, level: 1),
);
}
Map<String, dynamic> detected;
String? detectionWarning;
try {
detected = await compute(_extractCapFromPhotoIsolate, bytes);
} catch (e, st) {
debugPrint('[cap-photo] Detection isolate failed: $e\n$st');
detectionWarning =
'⚠️ Farberkennung ist fehlgeschlagen. Fallback-Farbe wird verwendet; bitte prüfen/korrigieren.';
final decoded = img.decodeImage(bytes);
final imageW = (decoded?.width ?? 1).toDouble();
final imageH = (decoded?.height ?? 1).toDouble();
final fallbackColor = Colors.orange.toARGB32();
detected = {
'dominantColor': fallbackColor,
'averageColor': fallbackColor,
'usedFallback': true,
'circleX': imageW / 2,
'circleY': imageH / 2,
'circleR': math.min(imageW, imageH) * 0.28,
'imageW': imageW,
'imageH': imageH,
'previewPng': bytes,
};
}
final bytes = await captured.readAsBytes();
final detected = await compute(_extractCapFromPhotoIsolate, bytes);
if (!mounted) return;
Color dominantColor = Color(detected['dominantColor'] as int);
@@ -201,23 +239,35 @@ class _MosaicHomePageState extends State<MosaicHomePage> {
if (updateDialog) {
setDialogState(() {});
}
final adjusted = await compute(
_extractCapFromAdjustedCircleIsolate,
<String, dynamic>{
'sourceBytes': bytes,
'circleX': circleX,
'circleY': circleY,
'circleR': circleR,
},
);
if (localToken != recalcToken) return;
dominantColor = Color(adjusted['dominantColor'] as int);
averageColor = Color(adjusted['averageColor'] as int);
previewBytes = adjusted['previewPng'] as Uint8List;
selected = mode == ColorExtractionMode.dominant
? dominantColor
: averageColor;
_photoCapHexCtrl.text = _colorToHex(selected);
try {
final adjusted = await compute(
_extractCapFromAdjustedCircleIsolate,
<String, dynamic>{
'sourceBytes': bytes,
'circleX': circleX,
'circleY': circleY,
'circleR': circleR,
},
);
if (localToken != recalcToken) return;
dominantColor = Color(adjusted['dominantColor'] as int);
averageColor = Color(adjusted['averageColor'] as int);
previewBytes = adjusted['previewPng'] as Uint8List;
selected = mode == ColorExtractionMode.dominant
? dominantColor
: averageColor;
_photoCapHexCtrl.text = _colorToHex(selected);
} catch (e, st) {
debugPrint('[cap-photo] Recalculate failed: $e\n$st');
if (localToken != recalcToken) return;
detectionWarning =
'⚠️ Aktualisierung fehlgeschlagen. Bitte Farbe manuell prüfen.';
const fallback = Colors.orange;
dominantColor = fallback;
averageColor = fallback;
selected = fallback;
_photoCapHexCtrl.text = _colorToHex(selected);
}
if (ctx.mounted) {
setDialogState(() {});
}
@@ -274,6 +324,16 @@ class _MosaicHomePageState extends State<MosaicHomePage> {
: 'Kreis erkannt. Direkt im Foto ziehen oder mit Pinch/Slider anpassen.',
style: Theme.of(context).textTheme.bodySmall,
),
if (detectionWarning != null) ...[
const SizedBox(height: 6),
Text(
detectionWarning!,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.error,
fontWeight: FontWeight.w600,
),
),
],
const SizedBox(height: 8),
SegmentedButton<ColorExtractionMode>(
segments: const [
@@ -1094,8 +1154,8 @@ class _CircleAdjustOverlayState extends State<_CircleAdjustOverlay> {
final x = (local.dx / maxW).clamp(0.0, 1.0);
final y = (local.dy / shownH).clamp(0.0, 1.0);
final scale = details.scale;
final r = ((_baseRadius ?? widget.circleR) * scale)
.clamp(0.06, 0.49);
final r =
((_baseRadius ?? widget.circleR) * scale).clamp(0.06, 0.49);
widget.onCircleChanged(x, y, r);
},
onScaleEnd: (_) => _baseRadius = null,
@@ -1125,7 +1185,8 @@ class _CircleOverlayPainter extends CustomPainter {
final double y;
final double r;
const _CircleOverlayPainter({required this.x, required this.y, required this.r});
const _CircleOverlayPainter(
{required this.x, required this.y, required this.r});
@override
void paint(Canvas canvas, Size size) {
@@ -1154,8 +1215,10 @@ class _CircleOverlayPainter extends CustomPainter {
..style = PaintingStyle.stroke
..strokeWidth = 1
..color = Colors.white70;
canvas.drawLine(Offset(center.dx - 10, center.dy), Offset(center.dx + 10, center.dy), cross);
canvas.drawLine(Offset(center.dx, center.dy - 10), Offset(center.dx, center.dy + 10), cross);
canvas.drawLine(Offset(center.dx - 10, center.dy),
Offset(center.dx + 10, center.dy), cross);
canvas.drawLine(Offset(center.dx, center.dy - 10),
Offset(center.dx, center.dy + 10), cross);
}
@override