feat: enhance ingredient management; add editable fields for quantity, unit, and notes in recipe creation
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -52,15 +52,22 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
late Map<int, int?> _selectedProductIds;
|
||||
late Map<int, String?> _selectedProductNames;
|
||||
|
||||
late Map<int, TextEditingController> _qtyControllers;
|
||||
late Map<int, TextEditingController> _unitControllers;
|
||||
late Map<int, TextEditingController> _noteControllers;
|
||||
|
||||
bool _isSaving = false;
|
||||
String? _saveError;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_markdownCtrl.dispose(); // always non-null after initState
|
||||
_markdownCtrl.dispose();
|
||||
if (_step == _Step.review) {
|
||||
_nameCtrl.dispose();
|
||||
_servingsCtrl.dispose();
|
||||
for (final c in _qtyControllers.values) c.dispose();
|
||||
for (final c in _unitControllers.values) c.dispose();
|
||||
for (final c in _noteControllers.values) c.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
@@ -71,11 +78,19 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
_included = List.generate(parsed.ingredients.length, (_) => true);
|
||||
_selectedProductIds = {};
|
||||
_selectedProductNames = {};
|
||||
_qtyControllers = {};
|
||||
_unitControllers = {};
|
||||
_noteControllers = {};
|
||||
for (var i = 0; i < parsed.ingredients.length; i++) {
|
||||
final suggestions = parsed.ingredients[i].suggestions;
|
||||
if (suggestions.isNotEmpty) {
|
||||
_selectedProductIds[i] = suggestions.first.productId;
|
||||
_selectedProductNames[i] = suggestions.first.productName;
|
||||
final ing = parsed.ingredients[i];
|
||||
_qtyControllers[i] = TextEditingController(
|
||||
text: ing.quantity > 0 ? formatQuantity(ing.quantity) : '',
|
||||
);
|
||||
_unitControllers[i] = TextEditingController(text: ing.unit);
|
||||
_noteControllers[i] = TextEditingController(text: ing.note ?? '');
|
||||
if (ing.suggestions.isNotEmpty) {
|
||||
_selectedProductIds[i] = ing.suggestions.first.productId;
|
||||
_selectedProductNames[i] = ing.suggestions.first.productName;
|
||||
} else {
|
||||
_selectedProductIds[i] = null;
|
||||
_selectedProductNames[i] = null;
|
||||
@@ -128,12 +143,17 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
if (!_included[i]) continue;
|
||||
final productId = _selectedProductIds[i];
|
||||
if (productId == null) continue;
|
||||
final ing = _parsed!.ingredients[i];
|
||||
final qty = double.tryParse(
|
||||
_qtyControllers[i]!.text.trim().replaceAll(',', '.'),
|
||||
) ??
|
||||
_parsed!.ingredients[i].quantity;
|
||||
final unit = _unitControllers[i]!.text.trim();
|
||||
final note = _noteControllers[i]!.text.trim();
|
||||
ingredients.add({
|
||||
'productId': productId,
|
||||
'quantity': ing.quantity,
|
||||
'unit': ing.unit,
|
||||
if (ing.note != null) 'note': ing.note,
|
||||
'quantity': qty,
|
||||
'unit': unit,
|
||||
if (note.isNotEmpty) 'note': note,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,28 +326,34 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
}
|
||||
|
||||
Widget _buildIngredientRow(int index, ParsedIngredient ing) {
|
||||
final qtyStr = ing.quantity > 0 ? '${formatQuantity(ing.quantity)} ' : '';
|
||||
final unitStr = ing.unit.isNotEmpty ? '${ing.unit} ' : '';
|
||||
final noteStr = ing.note != null ? ' (${ing.note})' : '';
|
||||
final label = '$qtyStr$unitStr${ing.rawName}$noteStr';
|
||||
final isIncluded = _included[index];
|
||||
final noProductFound = ing.suggestions.isEmpty;
|
||||
// Problem #2: tydlig varning om rad är inkluderad men saknar produkt
|
||||
final showMissingProductWarning = isIncluded && noProductFound;
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: CheckboxListTile(
|
||||
value: _included[index],
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CheckboxListTile(
|
||||
value: isIncluded,
|
||||
onChanged: (v) => setState(() => _included[index] = v ?? false),
|
||||
title: Text(label),
|
||||
subtitle: ing.suggestions.isEmpty
|
||||
title: Text(ing.rawName),
|
||||
subtitle: noProductFound
|
||||
? Text(
|
||||
context.l10n.recipeCreateNoProductFound,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 12),
|
||||
color: showMissingProductWarning
|
||||
? Theme.of(context).colorScheme.error
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 12,
|
||||
),
|
||||
)
|
||||
: DropdownButton<int>(
|
||||
value: _selectedProductIds[index],
|
||||
isExpanded: true,
|
||||
onChanged: _included[index]
|
||||
onChanged: isIncluded
|
||||
? (id) {
|
||||
if (id == null) return;
|
||||
setState(() {
|
||||
@@ -346,6 +372,59 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
// Problem #1: editerbara qty/unit/note-fält per ingrediens
|
||||
if (isIncluded)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 72,
|
||||
child: TextField(
|
||||
controller: _qtyControllers[index],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Mängd',
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(
|
||||
width: 72,
|
||||
child: TextField(
|
||||
controller: _unitControllers[index],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Enhet',
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _noteControllers[index],
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Not',
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user