diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1b6c10dd..77c192e5 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -480,6 +480,10 @@ "@selectIngredient": { "description": "Error message when the user hasn't selected an ingredient from the autocompleter" }, + "recentlyUsedIngredients": "Recently added ingredients", + "@recentlyUsedIngredients": { + "description": "A message when a user adds a new ingredient to a meal." + }, "selectImage": "Please select an image", "@selectImage": { "description": "Label and error message when the user hasn't selected an image to save" diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart index 88a98f89..be71cea5 100644 --- a/lib/models/nutrition/nutritional_plan.dart +++ b/lib/models/nutrition/nutritional_plan.dart @@ -21,6 +21,7 @@ import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/models/nutrition/log.dart'; import 'package:wger/models/nutrition/meal.dart'; +import 'package:wger/models/nutrition/meal_item.dart'; import 'nutritrional_values.dart'; @@ -134,4 +135,21 @@ class NutritionalPlan { return out; } + + /// Helper that returns all meal items for the current plan + /// + /// Duplicated ingredients are removed + List get allMealItems { + final List out = []; + for (final meal in meals) { + for (final mealItem in meal.mealItems) { + final ingredientInList = out.where((e) => e.ingredientId == mealItem.ingredientId); + + if (ingredientInList.isEmpty) { + out.add(mealItem); + } + } + } + return out; + } } diff --git a/lib/widgets/nutrition/forms.dart b/lib/widgets/nutrition/forms.dart index 16183407..3a0815e6 100644 --- a/lib/widgets/nutrition/forms.dart +++ b/lib/widgets/nutrition/forms.dart @@ -116,7 +116,9 @@ class MealItemForm extends StatelessWidget { final Meal _meal; late final MealItem _mealItem; - MealItemForm(this._meal, [mealItem]) { + final List _listMealItems; + + MealItemForm(this._meal, this._listMealItems, [mealItem]) { _mealItem = mealItem ?? MealItem.empty(); _mealItem.mealId = _meal.id!; } @@ -127,6 +129,7 @@ class MealItemForm extends StatelessWidget { @override Widget build(BuildContext context) { + final String unit = AppLocalizations.of(context).g; return Container( margin: const EdgeInsets.all(20), child: Form( @@ -200,6 +203,32 @@ class MealItemForm extends StatelessWidget { Navigator.of(context).pop(); }, ), + if (_listMealItems.isNotEmpty) const SizedBox(height: 10.0), + Container( + child: Text(AppLocalizations.of(context).recentlyUsedIngredients), + padding: const EdgeInsets.all(10.0), + ), + Expanded( + child: ListView.builder( + itemCount: _listMealItems.length, + shrinkWrap: true, + itemBuilder: (context, index) { + return Card( + child: ListTile( + onTap: () { + _ingredientController.text = _listMealItems[index].ingredientObj.name; + _amountController.text = _listMealItems[index].amount.toStringAsFixed(0); + _mealItem.ingredientId = _listMealItems[index].ingredientId; + _mealItem.amount = _listMealItems[index].amount; + }, + title: Text(_listMealItems[index].ingredientObj.name), + subtitle: Text('${_listMealItems[index].amount.toStringAsFixed(0)}$unit'), + trailing: const Icon(Icons.copy), + ), + ); + }, + ), + ) ], ), ), diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart index a7eb6bb4..432a52d1 100644 --- a/lib/widgets/nutrition/meal.dart +++ b/lib/widgets/nutrition/meal.dart @@ -30,9 +30,11 @@ import 'package:wger/widgets/nutrition/helpers.dart'; class MealWidget extends StatefulWidget { final Meal _meal; + final List _listMealItems; const MealWidget( this._meal, + this._listMealItems, ); @override @@ -127,7 +129,7 @@ class _MealWidgetState extends State { FormScreen.routeName, arguments: FormScreenArguments( AppLocalizations.of(context).addIngredient, - MealItemForm(widget._meal), + MealItemForm(widget._meal, widget._listMealItems), hasListView: true, ), ); diff --git a/lib/widgets/nutrition/nutritional_plan_detail.dart b/lib/widgets/nutrition/nutritional_plan_detail.dart index e0ebcd9a..f4a0722f 100644 --- a/lib/widgets/nutrition/nutritional_plan_detail.dart +++ b/lib/widgets/nutrition/nutritional_plan_detail.dart @@ -48,7 +48,9 @@ class NutritionalPlanDetailWidget extends StatelessWidget { delegate: SliverChildListDelegate( [ const SizedBox(height: 10), - ..._nutritionalPlan.meals.map((meal) => MealWidget(meal)).toList(), + ..._nutritionalPlan.meals + .map((meal) => MealWidget(meal, _nutritionalPlan.allMealItems)) + .toList(), Padding( padding: const EdgeInsets.all(8.0), child: ElevatedButton( diff --git a/test/nutritional_plan_model_test.dart b/test/nutritional_plan_model_test.dart index 3ff9d69a..b0fdc0b6 100644 --- a/test/nutritional_plan_model_test.dart +++ b/test/nutritional_plan_model_test.dart @@ -17,23 +17,32 @@ */ import 'package:flutter_test/flutter_test.dart'; +import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/models/nutrition/nutritrional_values.dart'; import '../test_data/nutritional_plans.dart'; void main() { + late NutritionalPlan plan; + + setUp(() { + plan = getNutritionalPlan(); + }); + group('model tests', () { test('Test the nutritionalValues method for nutritional plans', () { - final plan = getNutritionalPlan(); final values = NutritionalValues.values(4118.75, 32.75, 347.5, 9.5, 59.0, 37.75, 52.5, 30.5); expect(plan.nutritionalValues, values); }); test('Test the nutritionalValues method for meals', () { - final plan = getNutritionalPlan(); final meal = plan.meals.first; final values = NutritionalValues.values(518.75, 5.75, 17.5, 3.5, 29.0, 13.75, 49.5, 0.5); expect(meal.nutritionalValues, values); }); + + test('Test that the getter returns all meal items for a plan', () { + expect(plan.allMealItems, plan.meals[0].mealItems + plan.meals[1].mealItems); + }); }); }