fix(flow): guard stepper and reset state on image-less project load
This commit is contained in:
@@ -205,6 +205,31 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
return File('${projectsDir.path}/latest_project.json');
|
||||
}
|
||||
|
||||
bool get _hasSourceImage => _sourceImageBytes != null;
|
||||
|
||||
bool get _canGenerate => _hasSourceImage && !_isGenerating;
|
||||
|
||||
int get _maxAccessibleStepIndex =>
|
||||
_hasSourceImage ? MosaicFlowStep.result.index : MosaicFlowStep.image.index;
|
||||
|
||||
List<MosaicPaletteSnapshotEntry> _catalogSnapshotFromCurrentCatalog() {
|
||||
return _catalog
|
||||
.map((entry) => MosaicPaletteSnapshotEntry(
|
||||
name: entry.name,
|
||||
colorValue: entry.colorValue,
|
||||
))
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
void _showMissingImageHint() {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Bitte zuerst ein Bild auswählen.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
MosaicProjectData _buildProjectData() {
|
||||
return MosaicProjectData(
|
||||
useCapSize: _useCapSize,
|
||||
@@ -217,6 +242,7 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
colorVariation: _colorVariation,
|
||||
selectedPreset: _selectedPreset.name,
|
||||
sourceImageBytes: _sourceImageBytes,
|
||||
catalogSnapshot: _catalogSnapshotFromCurrentCatalog(),
|
||||
savedAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
@@ -288,6 +314,7 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
final data = MosaicProjectData.fromJson(
|
||||
jsonDecode(await file.readAsString()) as Map<String, dynamic>,
|
||||
);
|
||||
final hasCatalogSnapshot = data.catalogSnapshot.isNotEmpty;
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
@@ -305,18 +332,22 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
orElse: () => StylePreset.ausgewogen,
|
||||
);
|
||||
|
||||
if (data.sourceImageBytes != null) {
|
||||
_sourceImageBytes = data.sourceImageBytes;
|
||||
_result = null;
|
||||
_currentFlowStep = MosaicFlowStep.result;
|
||||
}
|
||||
_sourceImageBytes = data.sourceImageBytes;
|
||||
_result = null;
|
||||
_currentFlowStep =
|
||||
_sourceImageBytes == null ? MosaicFlowStep.image : MosaicFlowStep.result;
|
||||
_activeSection = HomeSection.mosaic;
|
||||
});
|
||||
|
||||
if (data.sourceImageBytes != null) await _generate();
|
||||
if (data.sourceImageBytes != null) {
|
||||
await _generate(catalogSnapshotOverride: hasCatalogSnapshot ? data.catalogSnapshot : null);
|
||||
}
|
||||
if (!silent && mounted) {
|
||||
final message = hasCatalogSnapshot
|
||||
? 'Projekt geladen ✅ (mit gespeichertem Katalog-Snapshot)'
|
||||
: 'Projekt geladen ✅ (ohne Snapshot: aktueller Katalog aktiv)';
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Projekt geladen ✅')),
|
||||
SnackBar(content: Text(message)),
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -879,8 +910,24 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
_scheduleRegenerate();
|
||||
}
|
||||
|
||||
Future<void> _generate() async {
|
||||
if (_sourceImageBytes == null || _catalog.isEmpty) return;
|
||||
Future<void> _generate({List<MosaicPaletteSnapshotEntry>? catalogSnapshotOverride}) async {
|
||||
if (_sourceImageBytes == null) {
|
||||
_showMissingImageHint();
|
||||
return;
|
||||
}
|
||||
|
||||
final paletteSource = catalogSnapshotOverride != null && catalogSnapshotOverride.isNotEmpty
|
||||
? catalogSnapshotOverride
|
||||
.map((entry) => <String, dynamic>{
|
||||
'name': entry.name,
|
||||
'value': entry.colorValue,
|
||||
})
|
||||
.toList(growable: false)
|
||||
: _catalog
|
||||
.map((p) => <String, dynamic>{'name': p.name, 'value': p.colorValue})
|
||||
.toList(growable: false);
|
||||
|
||||
if (paletteSource.isEmpty) return;
|
||||
|
||||
final int gridW = math.max(1, int.tryParse(_gridWidthCtrl.text) ?? 40);
|
||||
final int gridH = math.max(1, int.tryParse(_gridHeightCtrl.text) ?? 30);
|
||||
@@ -899,9 +946,7 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
'ditheringStrength': _ditheringStrength,
|
||||
'edgeEmphasis': _edgeEmphasis,
|
||||
'colorVariation': _colorVariation,
|
||||
'palette': _catalog
|
||||
.map((p) => <String, dynamic>{'name': p.name, 'value': p.colorValue})
|
||||
.toList(growable: false),
|
||||
'palette': paletteSource,
|
||||
};
|
||||
|
||||
final out = await compute(_generateMosaicIsolate, payload);
|
||||
@@ -952,7 +997,7 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
? FloatingActionButton.extended(
|
||||
backgroundColor: Colors.white.withValues(alpha: 0.85),
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
onPressed: _isGenerating ? null : _generate,
|
||||
onPressed: _canGenerate ? _generate : null,
|
||||
icon: _isGenerating
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
@@ -1085,8 +1130,12 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
);
|
||||
},
|
||||
onStepContinue: () {
|
||||
final next = _currentFlowStep.index + 1;
|
||||
if (next > _maxAccessibleStepIndex) {
|
||||
_showMissingImageHint();
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
final next = _currentFlowStep.index + 1;
|
||||
if (next <= MosaicFlowStep.result.index) {
|
||||
_currentFlowStep = MosaicFlowStep.values[next];
|
||||
}
|
||||
@@ -1101,6 +1150,10 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
});
|
||||
},
|
||||
onStepTapped: (index) {
|
||||
if (index > _maxAccessibleStepIndex) {
|
||||
_showMissingImageHint();
|
||||
return;
|
||||
}
|
||||
setState(() => _currentFlowStep = MosaicFlowStep.values[index]);
|
||||
},
|
||||
steps: [
|
||||
@@ -1250,7 +1303,7 @@ class _MosaicHomePageState extends State<MosaicHomePage>
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
key: const Key('generate-btn'),
|
||||
onPressed: _isGenerating ? null : _generate,
|
||||
onPressed: _canGenerate ? _generate : null,
|
||||
icon: const Icon(Icons.auto_fix_high_rounded),
|
||||
label: const Text('Generate Mosaic'),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user