mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
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
This commit is contained in:
@@ -169,7 +169,6 @@ class IngredientFormState extends State<IngredientForm> {
|
||||
final _timeController = TextEditingController(); // optional
|
||||
final _mealItem = MealItem.empty();
|
||||
|
||||
bool validIngredientId = false;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -287,7 +286,7 @@ class IngredientFormState extends State<IngredientForm> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (validIngredientId)
|
||||
if (ingredientIdController.text.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
@@ -302,10 +301,9 @@ class IngredientFormState extends State<IngredientForm> {
|
||||
builder: (BuildContext context, AsyncSnapshot<Ingredient> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
_mealItem.ingredient = snapshot.data!;
|
||||
return ListTile(
|
||||
leading: IngredientAvatar(ingredient: _mealItem.ingredient),
|
||||
title:
|
||||
Text(getShortNutritionValues(_mealItem.nutritionalValues, context)),
|
||||
return MealItemTile(
|
||||
ingredient: _mealItem.ingredient,
|
||||
nutritionalValues: _mealItem.nutritionalValues,
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Padding(
|
||||
@@ -365,14 +363,13 @@ class IngredientFormState extends State<IngredientForm> {
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
_ingredientController.text = widget.recent[index].ingredient.name;
|
||||
_ingredientIdController.text =
|
||||
widget.recent[index].ingredient.id.toString();
|
||||
_amountController.text = widget.recent[index].amount.toStringAsFixed(0);
|
||||
setState(() {
|
||||
_ingredientController.text = widget.recent[index].ingredient.name;
|
||||
_ingredientIdController.text =
|
||||
widget.recent[index].ingredient.id.toString();
|
||||
_amountController.text = widget.recent[index].amount.toStringAsFixed(0);
|
||||
_mealItem.ingredientId = widget.recent[index].ingredientId;
|
||||
_mealItem.amount = widget.recent[index].amount;
|
||||
validIngredientId = true;
|
||||
});
|
||||
},
|
||||
title: Text(
|
||||
|
||||
@@ -32,6 +32,7 @@ import 'package:wger/models/exercises/ingredient_api.dart';
|
||||
import 'package:wger/models/nutrition/ingredient.dart';
|
||||
import 'package:wger/models/nutrition/log.dart';
|
||||
import 'package:wger/models/nutrition/nutritional_plan.dart';
|
||||
import 'package:wger/models/nutrition/nutritional_values.dart';
|
||||
import 'package:wger/providers/nutrition.dart';
|
||||
import 'package:wger/widgets/core/core.dart';
|
||||
import 'package:wger/widgets/nutrition/helpers.dart';
|
||||
@@ -118,6 +119,11 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (value) {
|
||||
// if user changes the pattern, it means they want to drop the
|
||||
// currently loaded ingredient (if any) and start a new search
|
||||
widget._ingredientIdController.text = '';
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
labelText: AppLocalizations.of(context).searchIngredient,
|
||||
@@ -126,7 +132,8 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
|
||||
);
|
||||
},
|
||||
suggestionsCallback: (pattern) {
|
||||
if (pattern == '') {
|
||||
// don't do search if user has already loaded a specific item
|
||||
if (pattern == '' || widget._ingredientIdController.text.isNotEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -188,6 +195,7 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
|
||||
}
|
||||
final result = await Provider.of<NutritionPlansProvider>(context, listen: false)
|
||||
.searchIngredientWithCode(barcode);
|
||||
// TODO: show spinner...
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
@@ -198,7 +206,16 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
|
||||
builder: (ctx) => AlertDialog(
|
||||
key: const Key('found-dialog'),
|
||||
title: Text(AppLocalizations.of(context).productFound),
|
||||
content: Text(AppLocalizations.of(context).productFoundDescription(result.name)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context).productFoundDescription(result.name)),
|
||||
MealItemTile(
|
||||
ingredient: result,
|
||||
nutritionalValues: result.nutritionalValues,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
key: const Key('found-dialog-confirm-button'),
|
||||
@@ -357,3 +374,23 @@ class IngredientAvatar extends StatelessWidget {
|
||||
: const CircleIconAvatar(Icon(Icons.image, color: Colors.grey));
|
||||
}
|
||||
}
|
||||
|
||||
class MealItemTile extends StatelessWidget {
|
||||
final Ingredient ingredient;
|
||||
final NutritionalValues nutritionalValues;
|
||||
|
||||
const MealItemTile({
|
||||
super.key,
|
||||
required this.ingredient,
|
||||
required this.nutritionalValues,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: IngredientAvatar(ingredient: ingredient),
|
||||
title: Text(getShortNutritionValues(nutritionalValues, context)),
|
||||
subtitle: Text(ingredient.id.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user