Context
Deleting an entity that is referenced elsewhere is unguarded across the app. The most severe case: deleting an ingredient used in a recipe crashes at runtime (IngredientsProvider.get() null assertion in ingredients_provider.dart:43). Recipe deletion leaves stale Cooking entries in menus (displays "?" but no crash). Instruction deletion orphans input IDs in sibling instructions.
All three remove methods carry a // TODO: Ask for confirmation comment (ingredients_provider.dart:57, recipes_provider.dart:66, recipes_provider.dart:88).
| Entity deleted |
Referenced by |
Current behavior |
| Ingredient |
IngredientUsage in recipes |
Runtime crash (null assertion) |
| Recipe |
Cooking.recipeId in menu meals |
Graceful degradation (shows "?") |
| Instruction |
inputs list in sibling instructions |
Orphaned IDs (no crash, no cleanup) |
Proposed Solution
Add a pre-delete check and confirmation dialog pattern:
-
Model-level reference checks (parameterized, per ADRs 0001/0002): pure methods that answer "who references this entity?" without accessing providers. Ingredient.findReferencingRecipes(ingredientId:, recipes:), Recipe.findReferencingMeals(recipeId:, multiWeekMenu:), Recipe.findDependentInstructions(instructionId:, recipe:).
-
Reusable confirmation dialog (DeleteConfirmationDialog in flutter_essentials/): follows the existing showDialog<bool> + TextButton/FilledButton pattern from play_recipe_page.dart. Shows what references the entity and what will be affected. Skipped entirely when nothing references the entity (existing undo-snackbar UX preserved).
-
Cascade cleanup on confirmed delete: actively remove dangling references (ingredient usages from recipes, cooking entries from menus, orphaned input IDs from instructions) rather than leaving stale data.
-
Undo snackbar enhancement: capture and restore both the deleted entity and any cascade-cleaned references.
Key files to modify:
lib/ingredients/models/ingredient.dart -- add findReferencingRecipes()
lib/recipes/models/recipe.dart -- add findReferencingMeals(), findDependentInstructions()
lib/flutter_essentials/widgets/delete_confirmation_dialog.dart -- new reusable dialog
lib/ingredients/widgets/ingredients_page.dart -- pre-delete check at call site
lib/recipes/widgets/recipes_page.dart -- pre-delete check at call site
lib/recipes/widgets/recipe_page.dart -- pre-delete check for instruction deletion
Relevant ADRs: 0001 (model logic), 0002 (provider access restricted to UI), 0005 (instruction I/O chain), 0008 (multi-week menus), 0009 (recipe ID references).
Acceptance Criteria
Context
Deleting an entity that is referenced elsewhere is unguarded across the app. The most severe case: deleting an ingredient used in a recipe crashes at runtime (
IngredientsProvider.get()null assertion iningredients_provider.dart:43). Recipe deletion leaves staleCookingentries in menus (displays "?" but no crash). Instruction deletion orphans input IDs in sibling instructions.All three
removemethods carry a// TODO: Ask for confirmationcomment (ingredients_provider.dart:57,recipes_provider.dart:66,recipes_provider.dart:88).IngredientUsagein recipesCooking.recipeIdin menu mealsinputslist in sibling instructionsProposed Solution
Add a pre-delete check and confirmation dialog pattern:
Model-level reference checks (parameterized, per ADRs 0001/0002): pure methods that answer "who references this entity?" without accessing providers.
Ingredient.findReferencingRecipes(ingredientId:, recipes:),Recipe.findReferencingMeals(recipeId:, multiWeekMenu:),Recipe.findDependentInstructions(instructionId:, recipe:).Reusable confirmation dialog (
DeleteConfirmationDialoginflutter_essentials/): follows the existingshowDialog<bool>+ TextButton/FilledButton pattern fromplay_recipe_page.dart. Shows what references the entity and what will be affected. Skipped entirely when nothing references the entity (existing undo-snackbar UX preserved).Cascade cleanup on confirmed delete: actively remove dangling references (ingredient usages from recipes, cooking entries from menus, orphaned input IDs from instructions) rather than leaving stale data.
Undo snackbar enhancement: capture and restore both the deleted entity and any cascade-cleaned references.
Key files to modify:
lib/ingredients/models/ingredient.dart-- addfindReferencingRecipes()lib/recipes/models/recipe.dart-- addfindReferencingMeals(),findDependentInstructions()lib/flutter_essentials/widgets/delete_confirmation_dialog.dart-- new reusable dialoglib/ingredients/widgets/ingredients_page.dart-- pre-delete check at call sitelib/recipes/widgets/recipes_page.dart-- pre-delete check at call sitelib/recipes/widgets/recipe_page.dart-- pre-delete check for instruction deletionRelevant ADRs: 0001 (model logic), 0002 (provider access restricted to UI), 0005 (instruction I/O chain), 0008 (multi-week menus), 0009 (recipe ID references).
Acceptance Criteria