Skip to content

Commit b21c19a

Browse files
committed
various ingredient form UX issues
* upon scan completion, give more useful preview of the found ingredient * use ID field to track whether the form is "loaded" with a valid ingredient or not * don't trigger search if an ingredient is loaded, unless user changes the pattern * always show meal item preview, whether loaded from recent items, or from scan result
1 parent f00dc88 commit b21c19a

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

lib/widgets/nutrition/forms.dart

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ class IngredientFormState extends State<IngredientForm> {
169169
final _timeController = TextEditingController(); // optional
170170
final _mealItem = MealItem.empty();
171171

172-
bool validIngredientId = false;
173172
@override
174173
void initState() {
175174
super.initState();
@@ -287,7 +286,7 @@ class IngredientFormState extends State<IngredientForm> {
287286
),
288287
],
289288
),
290-
if (validIngredientId)
289+
if (ingredientIdController.text.isNotEmpty)
291290
Padding(
292291
padding: const EdgeInsets.all(8.0),
293292
child: Column(
@@ -302,10 +301,9 @@ class IngredientFormState extends State<IngredientForm> {
302301
builder: (BuildContext context, AsyncSnapshot<Ingredient> snapshot) {
303302
if (snapshot.hasData) {
304303
_mealItem.ingredient = snapshot.data!;
305-
return ListTile(
306-
leading: IngredientAvatar(ingredient: _mealItem.ingredient),
307-
title:
308-
Text(getShortNutritionValues(_mealItem.nutritionalValues, context)),
304+
return MealItemTile(
305+
ingredient: _mealItem.ingredient,
306+
nutritionalValues: _mealItem.nutritionalValues,
309307
);
310308
} else if (snapshot.hasError) {
311309
return Padding(
@@ -365,14 +363,13 @@ class IngredientFormState extends State<IngredientForm> {
365363
return Card(
366364
child: ListTile(
367365
onTap: () {
368-
_ingredientController.text = widget.recent[index].ingredient.name;
369-
_ingredientIdController.text =
370-
widget.recent[index].ingredient.id.toString();
371-
_amountController.text = widget.recent[index].amount.toStringAsFixed(0);
372366
setState(() {
367+
_ingredientController.text = widget.recent[index].ingredient.name;
368+
_ingredientIdController.text =
369+
widget.recent[index].ingredient.id.toString();
370+
_amountController.text = widget.recent[index].amount.toStringAsFixed(0);
373371
_mealItem.ingredientId = widget.recent[index].ingredientId;
374372
_mealItem.amount = widget.recent[index].amount;
375-
validIngredientId = true;
376373
});
377374
},
378375
title: Text(

lib/widgets/nutrition/widgets.dart

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import 'package:wger/models/exercises/ingredient_api.dart';
3232
import 'package:wger/models/nutrition/ingredient.dart';
3333
import 'package:wger/models/nutrition/log.dart';
3434
import 'package:wger/models/nutrition/nutritional_plan.dart';
35+
import 'package:wger/models/nutrition/nutritional_values.dart';
3536
import 'package:wger/providers/nutrition.dart';
3637
import 'package:wger/widgets/core/core.dart';
3738
import 'package:wger/widgets/nutrition/helpers.dart';
@@ -118,6 +119,11 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
118119
}
119120
return null;
120121
},
122+
onChanged: (value) {
123+
// if user changes the pattern, it means they want to drop the
124+
// currently loaded ingredient (if any) and start a new search
125+
widget._ingredientIdController.text = '';
126+
},
121127
decoration: InputDecoration(
122128
prefixIcon: const Icon(Icons.search),
123129
labelText: AppLocalizations.of(context).searchIngredient,
@@ -126,7 +132,8 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
126132
);
127133
},
128134
suggestionsCallback: (pattern) {
129-
if (pattern == '') {
135+
// don't do search if user has already loaded a specific item
136+
if (pattern == '' || widget._ingredientIdController.text.isNotEmpty) {
130137
return null;
131138
}
132139

@@ -188,6 +195,7 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
188195
}
189196
final result = await Provider.of<NutritionPlansProvider>(context, listen: false)
190197
.searchIngredientWithCode(barcode);
198+
// TODO: show spinner...
191199
if (!mounted) {
192200
return;
193201
}
@@ -198,7 +206,16 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
198206
builder: (ctx) => AlertDialog(
199207
key: const Key('found-dialog'),
200208
title: Text(AppLocalizations.of(context).productFound),
201-
content: Text(AppLocalizations.of(context).productFoundDescription(result.name)),
209+
content: Column(
210+
mainAxisSize: MainAxisSize.min,
211+
children: [
212+
Text(AppLocalizations.of(context).productFoundDescription(result.name)),
213+
MealItemTile(
214+
ingredient: result,
215+
nutritionalValues: result.nutritionalValues,
216+
),
217+
],
218+
),
202219
actions: [
203220
TextButton(
204221
key: const Key('found-dialog-confirm-button'),
@@ -357,3 +374,23 @@ class IngredientAvatar extends StatelessWidget {
357374
: const CircleIconAvatar(Icon(Icons.image, color: Colors.grey));
358375
}
359376
}
377+
378+
class MealItemTile extends StatelessWidget {
379+
final Ingredient ingredient;
380+
final NutritionalValues nutritionalValues;
381+
382+
const MealItemTile({
383+
super.key,
384+
required this.ingredient,
385+
required this.nutritionalValues,
386+
});
387+
388+
@override
389+
Widget build(BuildContext context) {
390+
return ListTile(
391+
leading: IngredientAvatar(ingredient: ingredient),
392+
title: Text(getShortNutritionValues(nutritionalValues, context)),
393+
subtitle: Text(ingredient.id.toString()),
394+
);
395+
}
396+
}

0 commit comments

Comments
 (0)