feat: enhance recipe ingredient model; add raw fields and optional properties for better ingredient handling
Test Suite / test (24.15.0) (push) Has been cancelled

This commit is contained in:
Nils-Johan Gynther
2026-05-06 07:25:42 +02:00
parent 612fcddb47
commit e4f201ea36
9 changed files with 349 additions and 267 deletions
@@ -2,7 +2,7 @@ enum IngredientStatus { enough, missing, unitMismatch }
class IngredientPreview {
final int ingredientId;
final int productId;
final int? productId;
final String productName;
final double requiredQuantity;
final String requiredUnit;
@@ -14,7 +14,7 @@ class IngredientPreview {
const IngredientPreview({
required this.ingredientId,
required this.productId,
this.productId,
required this.productName,
required this.requiredQuantity,
required this.requiredUnit,
@@ -34,8 +34,8 @@ class IngredientPreview {
};
return IngredientPreview(
ingredientId: json['ingredientId'] as int,
productId: json['productId'] as int,
productName: json['productName'] as String,
productId: (json['productId'] as num?)?.toInt(),
productName: (json['productName'] as String?) ?? (json['rawName'] as String? ?? ''),
requiredQuantity: (json['requiredQuantity'] as num).toDouble(),
requiredUnit: json['requiredUnit'] as String? ?? '',
note: json['note'] as String?,
@@ -19,6 +19,7 @@ class IngredientSuggestion {
class ParsedIngredient {
final String rawName;
final String? rawLine;
final double quantity;
final String unit;
final String? note;
@@ -27,6 +28,7 @@ class ParsedIngredient {
const ParsedIngredient({
required this.rawName,
this.rawLine,
required this.quantity,
required this.unit,
this.note,
@@ -38,6 +40,7 @@ class ParsedIngredient {
final rawSuggestions = json['suggestions'] as List<dynamic>? ?? [];
return ParsedIngredient(
rawName: json['rawName'] as String? ?? '',
rawLine: json['rawLine'] as String?,
quantity: (json['quantity'] as num? ?? 0).toDouble(),
unit: json['unit'] as String? ?? '',
note: json['note'] as String?,
@@ -1,15 +1,19 @@
class RecipeIngredient {
final int id;
final int productId;
final String productName;
final int? productId;
final String? productName;
final String rawName;
final String? rawLine;
final double quantity;
final String unit;
final String? note;
const RecipeIngredient({
required this.id,
required this.productId,
required this.productName,
this.productId,
this.productName,
required this.rawName,
this.rawLine,
required this.quantity,
required this.unit,
this.note,
@@ -20,8 +24,10 @@ class RecipeIngredient {
final rawQty = json['quantity'];
return RecipeIngredient(
id: (json['id'] as num).toInt(),
productId: (json['productId'] as num).toInt(),
productName: product?['name'] as String? ?? '',
productId: (json['productId'] as num?)?.toInt(),
productName: product?['canonicalName'] as String? ?? product?['name'] as String?,
rawName: json['rawName'] as String? ?? '',
rawLine: json['rawLine'] as String?,
quantity: rawQty is num
? rawQty.toDouble()
: double.tryParse(rawQty?.toString() ?? '') ?? 0,
@@ -191,7 +191,6 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
for (var i = 0; i < _parsed!.ingredients.length; i++) {
if (!_included[i]) continue;
final productId = _selectedProductIds[i];
if (productId == null) continue;
final qty = double.tryParse(
_qtyControllers[i]!.text.trim().replaceAll(',', '.'),
) ??
@@ -207,9 +206,11 @@ class _CreateRecipeScreenState extends ConsumerState<CreateRecipeScreen> {
.toList()
: <int>[];
ingredients.add({
'productId': productId,
'quantity': qty,
'unit': unit,
'rawName': ing.rawName,
if ((ing.rawLine ?? '').trim().isNotEmpty) 'rawLine': ing.rawLine,
if (productId != null) 'productId': productId,
if (qty > 0) 'quantity': qty,
if (unit.isNotEmpty) 'unit': unit,
if (note.isNotEmpty) 'note': note,
if (alternativeProductIds.isNotEmpty)
'alternativeProductIds': alternativeProductIds,
@@ -396,6 +396,10 @@ class _RecipeBody extends StatelessWidget {
const SizedBox(height: 12),
...recipe.ingredients.map((ing) {
final qtyStr = ing.quantity == 0 ? '' : _fmtQty(ing.quantity);
final ingredientLabel = (ing.rawName.trim().isNotEmpty
? ing.rawName
: (ing.productName ?? '').trim())
.trim();
final measureParts = [
if (qtyStr.isNotEmpty) qtyStr,
if (ing.unit.isNotEmpty) ing.unit,
@@ -430,8 +434,8 @@ class _RecipeBody extends StatelessWidget {
Expanded(
child: Text(
ing.note != null
? '${ing.productName} (${ing.note})'
: ing.productName,
? '$ingredientLabel (${ing.note})'
: ingredientLabel,
style: theme.textTheme.bodyMedium,
),
),