diff --git a/lib/models/nutrition/nutritional_plan.dart b/lib/models/nutrition/nutritional_plan.dart
index fe453dd2e..a4527ec1d 100644
--- a/lib/models/nutrition/nutritional_plan.dart
+++ b/lib/models/nutrition/nutritional_plan.dart
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:json_annotation/json_annotation.dart';
@@ -201,16 +202,16 @@ class NutritionalPlan {
return out;
}
- /// Helper that returns all meal items for the current plan
- ///
- /// Duplicated ingredients are removed
- List get allMealItems {
+ /// returns meal items across all meals
+ /// deduped by the combination of amount and ingredient ID
+ List get dedupMealItems {
final List out = [];
for (final meal in meals) {
for (final mealItem in meal.mealItems) {
- final ingredientInList = out.where((e) => e.ingredientId == mealItem.ingredientId);
+ final found = out.firstWhereOrNull(
+ (e) => e.amount == mealItem.amount && e.ingredientId == mealItem.ingredientId);
- if (ingredientInList.isEmpty) {
+ if (found == null) {
out.add(mealItem);
}
}
@@ -218,6 +219,20 @@ class NutritionalPlan {
return out;
}
+ /// returns diary entries
+ /// deduped by the combination of amount and ingredient ID
+ List get dedupDiaryEntries {
+ final out = [];
+ for (final log in diaryEntries) {
+ final found =
+ out.firstWhereOrNull((e) => e.amount == log.amount && e.ingredientId == log.ingredientId);
+ if (found == null) {
+ out.add(log);
+ }
+ }
+ return out;
+ }
+
Meal pseudoMealOthers(String name) {
return Meal(
id: PSEUDO_MEAL_ID,
diff --git a/lib/widgets/nutrition/forms.dart b/lib/widgets/nutrition/forms.dart
index da61afc64..ba96b5cd1 100644
--- a/lib/widgets/nutrition/forms.dart
+++ b/lib/widgets/nutrition/forms.dart
@@ -131,7 +131,7 @@ Widget MealItemForm(Meal meal, List recent, [String? barcode, bool? te
Widget IngredientLogForm(NutritionalPlan plan) {
return IngredientForm(
- recent: plan.diaryEntries,
+ recent: plan.dedupDiaryEntries,
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
Provider.of(context, listen: false)
.logIngredientToDiary(mealItem, plan.id!, dt);
@@ -168,6 +168,7 @@ class IngredientFormState extends State {
final _dateController = TextEditingController(); // optional
final _timeController = TextEditingController(); // optional
final _mealItem = MealItem.empty();
+ var _searchQuery = ''; // copy from typeahead. for filtering suggestions
@override
void initState() {
@@ -201,10 +202,18 @@ class IngredientFormState extends State {
});
}
+ void updateSearchQuery(String query) {
+ setState(() {
+ _searchQuery = query;
+ });
+ }
+
@override
Widget build(BuildContext context) {
final String unit = AppLocalizations.of(context).g;
-
+ final queryLower = _searchQuery.toLowerCase();
+ final suggestions =
+ widget.recent.where((e) => e.ingredient.name.toLowerCase().contains(queryLower)).toList();
return Container(
margin: const EdgeInsets.all(20),
child: Form(
@@ -218,6 +227,7 @@ class IngredientFormState extends State {
test: widget.test,
selectIngredient: selectIngredient,
unSelectIngredient: unSelectIngredient,
+ updateSearchQuery: updateSearchQuery,
),
Row(
children: [
@@ -370,27 +380,26 @@ class IngredientFormState extends State {
Navigator.of(context).pop();
},
),
- if (widget.recent.isNotEmpty) const SizedBox(height: 10.0),
+ if (suggestions.isNotEmpty) const SizedBox(height: 10.0),
Container(
padding: const EdgeInsets.all(10.0),
child: Text(AppLocalizations.of(context).recentlyUsedIngredients),
),
Expanded(
child: ListView.builder(
- itemCount: widget.recent.length,
+ itemCount: suggestions.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {
- final ingredient = widget.recent[index].ingredient;
- selectIngredient(
- ingredient.id, ingredient.name, widget.recent[index].amount);
+ final ingredient = suggestions[index].ingredient;
+ selectIngredient(ingredient.id, ingredient.name, suggestions[index].amount);
},
title: Text(
- '${widget.recent[index].ingredient.name} (${widget.recent[index].amount.toStringAsFixed(0)}$unit)'),
+ '${suggestions[index].ingredient.name} (${suggestions[index].amount.toStringAsFixed(0)}$unit)'),
subtitle: Text(getShortNutritionValues(
- widget.recent[index].ingredient.nutritionalValues, context)),
+ suggestions[index].ingredient.nutritionalValues, context)),
trailing: const Icon(Icons.copy),
),
);
diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart
index 769f991fd..e11b6e7c0 100644
--- a/lib/widgets/nutrition/meal.dart
+++ b/lib/widgets/nutrition/meal.dart
@@ -39,11 +39,11 @@ enum viewMode {
class MealWidget extends StatefulWidget {
final Meal _meal;
- final List _listMealItems;
+ final List _recentMealItems;
const MealWidget(
this._meal,
- this._listMealItems,
+ this._recentMealItems,
);
@override
@@ -108,7 +108,7 @@ class _MealWidgetState extends State {
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).addIngredient,
- MealItemForm(widget._meal, widget._listMealItems),
+ MealItemForm(widget._meal, widget._recentMealItems),
hasListView: true,
),
);
diff --git a/lib/widgets/nutrition/nutritional_plan_detail.dart b/lib/widgets/nutrition/nutritional_plan_detail.dart
index 9feb631c4..cf8739040 100644
--- a/lib/widgets/nutrition/nutritional_plan_detail.dart
+++ b/lib/widgets/nutrition/nutritional_plan_detail.dart
@@ -56,11 +56,11 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
const SizedBox(height: 10),
..._nutritionalPlan.meals.map((meal) => MealWidget(
meal,
- _nutritionalPlan.allMealItems,
+ _nutritionalPlan.dedupMealItems,
)),
MealWidget(
_nutritionalPlan.pseudoMealOthers('Other logs'),
- _nutritionalPlan.allMealItems,
+ _nutritionalPlan.dedupMealItems,
),
if (!_nutritionalPlan.onlyLogging)
Padding(
diff --git a/lib/widgets/nutrition/widgets.dart b/lib/widgets/nutrition/widgets.dart
index 35d335c58..460c15a7e 100644
--- a/lib/widgets/nutrition/widgets.dart
+++ b/lib/widgets/nutrition/widgets.dart
@@ -65,6 +65,7 @@ class IngredientTypeahead extends StatefulWidget {
final Function(int id, String name, num? amount) selectIngredient;
final Function() unSelectIngredient;
+ final Function(String query) updateSearchQuery;
const IngredientTypeahead(
this._ingredientIdController,
@@ -74,6 +75,7 @@ class IngredientTypeahead extends StatefulWidget {
this.barcode = '',
required this.selectIngredient,
required this.unSelectIngredient,
+ required this.updateSearchQuery,
});
@override
@@ -125,6 +127,7 @@ class _IngredientTypeaheadState extends State {
return null;
},
onChanged: (value) {
+ widget.updateSearchQuery(value);
// unselect to start a new search
widget.unSelectIngredient();
},
diff --git a/test/nutrition/nutritional_plan_model_test.dart b/test/nutrition/nutritional_plan_model_test.dart
index 860e47dc8..a48fa4683 100644
--- a/test/nutrition/nutritional_plan_model_test.dart
+++ b/test/nutrition/nutritional_plan_model_test.dart
@@ -101,7 +101,7 @@ void main() {
});
test('Test that the getter returns all meal items for a plan', () {
- expect(plan.allMealItems, plan.meals[0].mealItems + plan.meals[1].mealItems);
+ expect(plan.dedupMealItems, plan.meals[0].mealItems + plan.meals[1].mealItems);
});
});
}