From 68799b01123e7a67f6f3939cebf19859503c0782 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Mon, 27 May 2024 23:52:46 +0200 Subject: [PATCH] log meal: separate confirmation page with portion choice --- lib/main.dart | 2 + lib/models/nutrition/meal.dart | 18 ++++ lib/models/nutrition/meal_item.dart | 21 ++++ lib/screens/log_meal_screen.dart | 146 ++++++++++++++++++++++++++++ lib/widgets/nutrition/meal.dart | 20 ++-- 5 files changed, 196 insertions(+), 11 deletions(-) create mode 100644 lib/screens/log_meal_screen.dart diff --git a/lib/main.dart b/lib/main.dart index 9c45f0b87..c035e5e52 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -38,6 +38,7 @@ import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/gallery_screen.dart'; import 'package:wger/screens/gym_mode.dart'; import 'package:wger/screens/home_tabs_screen.dart'; +import 'package:wger/screens/log_meal_screen.dart'; import 'package:wger/screens/measurement_categories_screen.dart'; import 'package:wger/screens/measurement_entries_screen.dart'; import 'package:wger/screens/nutritional_diary_screen.dart'; @@ -158,6 +159,7 @@ class MyApp extends StatelessWidget { NutritionalPlansScreen.routeName: (ctx) => NutritionalPlansScreen(), NutritionalDiaryScreen.routeName: (ctx) => NutritionalDiaryScreen(), NutritionalPlanScreen.routeName: (ctx) => NutritionalPlanScreen(), + LogMealScreen.routeName: (ctx) => LogMealScreen(), WeightScreen.routeName: (ctx) => WeightScreen(), WorkoutPlanScreen.routeName: (ctx) => WorkoutPlanScreen(), WorkoutPlansScreen.routeName: (ctx) => WorkoutPlansScreen(), diff --git a/lib/models/nutrition/meal.dart b/lib/models/nutrition/meal.dart index a90b83c06..ecb3ca3f5 100644 --- a/lib/models/nutrition/meal.dart +++ b/lib/models/nutrition/meal.dart @@ -88,4 +88,22 @@ class Meal { factory Meal.fromJson(Map json) => _$MealFromJson(json); Map toJson() => _$MealToJson(this); + + Meal copyWith({ + int? id, + int? planId, + TimeOfDay? time, + String? name, + List? mealItems, + List? diaryEntries, + }) { + return Meal( + id: id ?? this.id, + plan: planId ?? this.planId, + time: time ?? this.time, + name: name ?? this.name, + mealItems: mealItems ?? this.mealItems, + diaryEntries: diaryEntries ?? this.diaryEntries, + ); + } } diff --git a/lib/models/nutrition/meal_item.dart b/lib/models/nutrition/meal_item.dart index 4bf10e94b..6ac0ebd5b 100644 --- a/lib/models/nutrition/meal_item.dart +++ b/lib/models/nutrition/meal_item.dart @@ -91,4 +91,25 @@ class MealItem { return out; } + + MealItem copyWith({ + int? id, + int? mealId, + int? ingredientId, + int? weightUnitId, + num? amount, + Ingredient? ingredient, + IngredientWeightUnit? weightUnitObj, + }) { + final m = MealItem( + id: id ?? this.id, + mealId: mealId ?? this.mealId, + ingredientId: ingredientId ?? this.ingredientId, + weightUnitId: weightUnitId ?? this.weightUnitId, + amount: amount ?? this.amount, + ingredient: ingredient ?? this.ingredient, + ); + m.weightUnitObj = weightUnitObj ?? this.weightUnitObj; + return m; + } } diff --git a/lib/screens/log_meal_screen.dart b/lib/screens/log_meal_screen.dart new file mode 100644 index 000000000..54e927b63 --- /dev/null +++ b/lib/screens/log_meal_screen.dart @@ -0,0 +1,146 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wger Workout Manager is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:provider/provider.dart'; +import 'package:wger/models/nutrition/meal.dart'; +import 'package:wger/models/nutrition/meal_item.dart'; +import 'package:wger/providers/nutrition.dart'; +import 'package:wger/widgets/nutrition/meal.dart'; +import 'package:wger/widgets/nutrition/widgets.dart'; + +class LogMealArguments { + final Meal meal; + + LogMealArguments(this.meal); +} + +class LogMealScreen extends StatefulWidget { + static const routeName = '/log-meal'; + + @override + State createState() => _LogMealScreenState(); +} + +class _LogMealScreenState extends State { + late TextEditingController _controller; + int portionPct = 100; + + @override + void initState() { + super.initState(); + _controller = TextEditingController(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final args = ModalRoute.of(context)!.settings.arguments as LogMealArguments; + _controller.text = portionPct.toString(); + final meal = args.meal.copyWith( + mealItems: args.meal.mealItems + .map((mealItem) => mealItem.copyWith(amount: mealItem.amount * portionPct / 100)) + .toList()); + + return Scaffold( + appBar: AppBar( + title: Text('Log meal to diary'), + ), + body: Consumer( + builder: (context, nutritionProvider, child) => SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Text(meal.name, style: Theme.of(context).textTheme.headlineSmall), + if (meal.mealItems.isEmpty) + const ListTile(title: Text('No ingredients defined yet')) + else + Column( + children: [ + const NutritionDiaryheader(), + ...meal.mealItems + .map((item) => MealItemWidget(item, viewMode.withAllDetails, false)), + Row( + children: [ + Text('Portion size'), + Expanded( + child: TextField( + maxLength: 4, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: 'Enter the portion size as a percent', + ), + controller: _controller, + onChanged: (value) { + var v = int.tryParse(value); + if (v == null) return; + setState(() { + portionPct = v; + }); + }, + ), + ), + ], + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (meal.mealItems.isNotEmpty) + TextButton( + child: const Text('Log'), + onPressed: () async { + await Provider.of(context, listen: false) + .logMealToDiary(meal); + // ignore: use_build_context_synchronously + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + // ignore: use_build_context_synchronously + AppLocalizations.of(context).mealLogged, + textAlign: TextAlign.center, + ), + ), + ); + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text(MaterialLocalizations.of(context).cancelButtonLabel), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart index 253fde07b..769f991fd 100644 --- a/lib/widgets/nutrition/meal.dart +++ b/lib/widgets/nutrition/meal.dart @@ -25,6 +25,7 @@ import 'package:wger/models/nutrition/meal.dart'; import 'package:wger/models/nutrition/meal_item.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/screens/form_screen.dart'; +import 'package:wger/screens/log_meal_screen.dart'; import 'package:wger/widgets/nutrition/charts.dart'; import 'package:wger/widgets/nutrition/forms.dart'; import 'package:wger/widgets/nutrition/helpers.dart'; @@ -360,17 +361,14 @@ class MealHeader extends StatelessWidget { ) ], ), - onTap: () { - Provider.of(context, listen: false).logMealToDiary(_meal); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).mealLogged, - textAlign: TextAlign.center, - ), - ), - ); - }, + onTap: _meal.isRealMeal + ? () { + Navigator.of(context).pushNamed( + LogMealScreen.routeName, + arguments: LogMealArguments(_meal), + ); + } + : null, ), ], );