Skip to content

Commit

Permalink
Merge pull request #593 from wger-project/log-fractional-meals
Browse files Browse the repository at this point in the history
log meal: separate confirmation page with portion choice
  • Loading branch information
rolandgeider authored May 29, 2024
2 parents 3e138d2 + 68799b0 commit 7f4dafd
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 11 deletions.
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(),
Expand Down
18 changes: 18 additions & 0 deletions lib/models/nutrition/meal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,22 @@ class Meal {
factory Meal.fromJson(Map<String, dynamic> json) => _$MealFromJson(json);

Map<String, dynamic> toJson() => _$MealToJson(this);

Meal copyWith({
int? id,
int? planId,
TimeOfDay? time,
String? name,
List<MealItem>? mealItems,
List<Log>? 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,
);
}
}
21 changes: 21 additions & 0 deletions lib/models/nutrition/meal_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
146 changes: 146 additions & 0 deletions lib/screens/log_meal_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/

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<LogMealScreen> createState() => _LogMealScreenState();
}

class _LogMealScreenState extends State<LogMealScreen> {
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<NutritionPlansProvider>(
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<NutritionPlansProvider>(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(),
),
],
),
],
),
),
),
),
);
}
}
20 changes: 9 additions & 11 deletions lib/widgets/nutrition/meal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -360,17 +361,14 @@ class MealHeader extends StatelessWidget {
)
],
),
onTap: () {
Provider.of<NutritionPlansProvider>(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,
),
],
);
Expand Down

0 comments on commit 7f4dafd

Please sign in to comment.