proper nullable "nutritional goals" with inference

differentiate between a goal being set (but as 0) vs a goal not being
set. fixes various correctness issues
This commit is contained in:
Dieter Plaetinck
2024-05-03 15:15:24 +02:00
parent 00b5cc2f57
commit 36697a660d
6 changed files with 264 additions and 118 deletions

View File

@@ -0,0 +1,142 @@
/*
* 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.
*
* This program 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:wger/helpers/consts.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';
class NutritionalGoals {
double? energy = 0;
double? protein = 0;
double? carbohydrates = 0;
double? carbohydratesSugar = 0;
double? fat = 0;
double? fatSaturated = 0;
double? fibres = 0;
double? sodium = 0;
NutritionalGoals({
this.energy,
this.protein,
this.carbohydrates,
this.carbohydratesSugar,
this.fat,
this.fatSaturated,
this.fibres,
this.sodium,
}) {
// infer values where we can
if (energy == null) {
if (protein != null && carbohydrates != null && fat != null) {
energy =
protein! * ENERGY_PROTEIN + carbohydrates! * ENERGY_CARBOHYDRATES + fat! * ENERGY_FAT;
}
return;
}
// TODO: input validation when the user modifies/creates the plan, to assure energy is high enough
if (protein == null && carbohydrates != null && fat != null) {
protein =
(energy! - carbohydrates! * ENERGY_CARBOHYDRATES - fat! * ENERGY_FAT) / ENERGY_PROTEIN;
assert(protein! > 0);
} else if (carbohydrates == null && protein != null && fat != null) {
carbohydrates =
(energy! - protein! * ENERGY_PROTEIN - fat! * ENERGY_FAT) / ENERGY_CARBOHYDRATES;
assert(carbohydrates! > 0);
} else if (fat == null && protein != null && carbohydrates != null) {
fat = (energy! - protein! * ENERGY_PROTEIN - carbohydrates! * ENERGY_CARBOHYDRATES) /
ENERGY_FAT;
assert(fat! > 0);
}
}
NutritionalGoals operator /(double v) {
return NutritionalGoals(
energy: energy != null ? energy! / v : null,
protein: protein != null ? protein! / v : null,
carbohydrates: carbohydrates != null ? carbohydrates! / v : null,
carbohydratesSugar: carbohydratesSugar != null ? carbohydratesSugar! / v : null,
fat: fat != null ? fat! / v : null,
fatSaturated: fatSaturated != null ? fatSaturated! / v : null,
fibres: fibres != null ? fibres! / v : null,
sodium: sodium != null ? sodium! / v : null,
);
}
bool isComplete() {
return energy != null && protein != null && carbohydrates != null && fat != null;
}
/// Convert goals into values.
/// This turns unset goals into values of 0.
/// Only use this if you know what you're doing, e.g. if isComplete() is true
NutritionalValues toValues() {
return NutritionalValues.values(
energy ?? 0,
protein ?? 0,
carbohydrates ?? 0,
carbohydratesSugar ?? 0,
fat ?? 0,
fatSaturated ?? 0,
fibres ?? 0,
sodium ?? 0,
);
}
/// Calculates the percentage each macro nutrient adds to the total energy
NutritionalGoals energyPercentage() {
final goals = NutritionalGoals();
if (energy == null) {
return goals;
}
assert(energy! > 0);
if (protein != null) {
goals.protein = (100 * protein! * ENERGY_PROTEIN) / energy!;
}
if (carbohydrates != null) {
goals.carbohydrates = (100 * carbohydrates! * ENERGY_CARBOHYDRATES) / energy!;
}
if (fat != null) {
goals.fat = (100 * fat! * ENERGY_FAT) / energy!;
}
return goals;
}
double? prop(String name) {
return switch (name) {
'energy' => energy,
'protein' => protein,
'carbohydrates' => carbohydrates,
'carbohydratesSugar' => carbohydratesSugar,
'fat' => fat,
'fatSaturated' => fatSaturated,
'fibres' => fibres,
'sodium' => sodium,
_ => 0,
};
}
@override
String toString() {
return 'e: $energy, p: $protein, c: $carbohydrates, cS: $carbohydratesSugar, f: $fat, fS: $fatSaturated, fi: $fibres, s: $sodium';
}
@override
//ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hash(
energy, protein, carbohydrates, carbohydratesSugar, fat, fatSaturated, fibres, sodium);
}

View File

@@ -24,6 +24,7 @@ import 'package:wger/helpers/json.dart';
import 'package:wger/models/nutrition/log.dart';
import 'package:wger/models/nutrition/meal.dart';
import 'package:wger/models/nutrition/meal_item.dart';
import 'package:wger/models/nutrition/nutritional_goals.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';
part 'nutritional_plan.g.dart';
@@ -107,19 +108,28 @@ class NutritionalPlan {
/// note that (some of) this is already done on the server. It might be better
/// to read it from there, but on the other hand we might want to do more locally
/// so that a mostly offline mode is possible.
NutritionalValues get plannedNutritionalValues {
NutritionalGoals get nutritionalGoals {
// If there are set goals, they take preference over any meals
if (hasAnyGoals) {
final out = NutritionalValues();
out.energy = goalEnergy != null ? goalEnergy!.toDouble() : 0;
out.fat = goalFat != null ? goalFat!.toDouble() : 0;
out.carbohydrates = goalCarbohydrates != null ? goalCarbohydrates!.toDouble() : 0;
out.protein = goalProtein != null ? goalProtein!.toDouble() : 0;
return out;
return NutritionalGoals(
energy: goalEnergy?.toDouble(),
fat: goalFat?.toDouble(),
protein: goalProtein?.toDouble(),
carbohydrates: goalCarbohydrates?.toDouble(),
);
}
return meals.fold(NutritionalValues(), (a, b) => a + b.plannedNutritionalValues);
// otherwise, add up all the nutritional values of the meals and use that as goals
final sumValues = meals.fold(NutritionalValues(), (a, b) => a + b.plannedNutritionalValues);
return NutritionalGoals(
energy: sumValues.energy,
fat: sumValues.fat,
protein: sumValues.protein,
carbohydrates: sumValues.carbohydrates,
carbohydratesSugar: sumValues.carbohydratesSugar,
fatSaturated: sumValues.fatSaturated,
fibres: sumValues.fibres,
sodium: sumValues.sodium,
);
}
NutritionalValues get loggedNutritionalValuesToday {
@@ -138,28 +148,6 @@ class NutritionalPlan {
.fold(NutritionalValues(), (a, b) => a + b.nutritionalValues);
}
/// Calculates the percentage each macro nutrient adds to the total energy
BaseNutritionalValues energyPercentage(NutritionalValues values) {
return BaseNutritionalValues(
values.protein > 0 ? ((values.protein * ENERGY_PROTEIN * 100) / values.energy) : 0,
values.carbohydrates > 0
? ((values.carbohydrates * ENERGY_CARBOHYDRATES * 100) / values.energy)
: 0,
values.fat > 0 ? ((values.fat * ENERGY_FAT * 100) / values.energy) : 0,
);
}
/// Calculates the grams per body-kg for each macro nutrient
BaseNutritionalValues gPerBodyKg(num weight, NutritionalValues values) {
assert(weight > 0);
return BaseNutritionalValues(
values.protein / weight,
values.carbohydrates / weight,
values.fat / weight,
);
}
Map<DateTime, NutritionalValues> get logEntriesValues {
final out = <DateTime, NutritionalValues>{};
for (final log in diaryEntries) {

View File

@@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:math';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -50,7 +52,7 @@ class FlNutritionalPlanGoalWidgetState extends State<FlNutritionalPlanGoalWidget
// why don't we just handle this inside this function? because it might be
// *another* gauge that's in surplus and we want to have consistent widths
// between all gauges
Widget _DIYGauge(BuildContext context, double normWidth, num? plan, double val) {
Widget _DIYGauge(BuildContext context, double normWidth, double? plan, double val) {
Container segment(double width, Color color) {
return Container(
height: 16,
@@ -82,59 +84,52 @@ class FlNutritionalPlanGoalWidgetState extends State<FlNutritionalPlanGoalWidget
@override
Widget build(BuildContext context) {
final plan = widget._nutritionalPlan;
final goals = plan.nutritionalGoals;
final today = plan.loggedNutritionalValuesToday;
return LayoutBuilder(builder: (context, constraints) {
var maxVal = 1.0;
// if any of the bars goes over 100%, find the one that goes over the most
// that one needs the most horizontal space to show how much it goes over,
// and therefore reduces the width of "100%" the most, and this width we want
// to be consistent for all other bars
if (plan.goalProtein != null && today.protein / plan.goalProtein! > maxVal) {
maxVal = today.protein / plan.goalProtein!;
}
if (plan.goalCarbohydrates != null &&
today.carbohydrates / plan.goalCarbohydrates! > maxVal) {
maxVal = today.carbohydrates / plan.goalCarbohydrates!;
}
if (plan.goalFat != null && today.fat / plan.goalFat! > maxVal) {
maxVal = today.fat / plan.goalFat!;
}
if (plan.goalEnergy != null && today.energy / plan.goalEnergy! > maxVal) {
maxVal = today.energy / plan.goalEnergy!;
}
// if any of the bars goes over 100%, find the one that goes over the most
// that one needs the most horizontal space to show how much it goes over,
// and therefore reduces the width of "100%" the most, and this width we want
// to be consistent for all other bars.
// if none goes over, 100% means fill all available space
final maxVal = [
1.0,
if (goals.protein != null && goals.protein! > 0) today.protein / goals.protein!,
if (goals.carbohydrates != null && goals.carbohydrates! > 0)
today.carbohydrates / goals.carbohydrates!,
if (goals.fat != null && goals.fat! > 0) today.fat / goals.fat!,
if (goals.energy != null && goals.energy! > 0) today.energy / goals.energy!
].reduce(max);
final normWidth = constraints.maxWidth / maxVal;
String fmtMacro(String name, double today, double? goal, String unit) {
return '$name: ${today.toStringAsFixed(0)}${goal == null ? '' : ' /${goal.toStringAsFixed(0)}'} $unit';
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(plan.goalProtein == null
? '${AppLocalizations.of(context).protein}: ${today.protein.toStringAsFixed(0)} ${AppLocalizations.of(context).g}'
: '${AppLocalizations.of(context).protein}: ${today.protein.toStringAsFixed(0)} / ${plan.goalProtein} ${AppLocalizations.of(context).g}'),
Text(fmtMacro(AppLocalizations.of(context).protein, today.protein, goals.protein,
AppLocalizations.of(context).g)),
const SizedBox(height: 2),
_DIYGauge(context, normWidth, plan.goalProtein, today.protein),
_DIYGauge(context, normWidth, goals.protein, today.protein),
const SizedBox(height: 8),
Text(plan.goalCarbohydrates == null
? '${AppLocalizations.of(context).carbohydrates}: ${today.carbohydrates.toStringAsFixed(0)} ${AppLocalizations.of(context).g}'
: '${AppLocalizations.of(context).carbohydrates}: ${today.carbohydrates.toStringAsFixed(0)} / ${plan.goalCarbohydrates} ${AppLocalizations.of(context).g}'),
Text(fmtMacro(AppLocalizations.of(context).carbohydrates, today.carbohydrates,
goals.carbohydrates, AppLocalizations.of(context).g)),
const SizedBox(height: 2),
_DIYGauge(context, normWidth, plan.goalCarbohydrates, today.carbohydrates),
_DIYGauge(context, normWidth, goals.carbohydrates, today.carbohydrates),
const SizedBox(height: 8),
Text(plan.goalFat == null
? '${AppLocalizations.of(context).fat}: ${today.fat.toStringAsFixed(0)} ${AppLocalizations.of(context).g}'
: '${AppLocalizations.of(context).fat}: ${today.fat.toStringAsFixed(0)} / ${plan.goalFat} ${AppLocalizations.of(context).g}'),
Text(fmtMacro(AppLocalizations.of(context).fat, today.fat, goals.fat,
AppLocalizations.of(context).g)),
const SizedBox(height: 2),
_DIYGauge(context, normWidth, plan.goalFat, today.fat),
_DIYGauge(context, normWidth, goals.fat, today.fat),
const SizedBox(height: 8),
Text(plan.goalEnergy == null
? '${AppLocalizations.of(context).energy}: ${today.energy.toStringAsFixed(0)} ${AppLocalizations.of(context).kcal}'
: '${AppLocalizations.of(context).energy}: ${today.energy.toStringAsFixed(0)} / ${plan.goalEnergy} ${AppLocalizations.of(context).kcal}'),
Text(fmtMacro(AppLocalizations.of(context).energy, today.energy, goals.energy,
AppLocalizations.of(context).kcal)),
const SizedBox(height: 2),
_DIYGauge(context, normWidth, plan.goalEnergy, today.energy),
_DIYGauge(context, normWidth, goals.energy, today.energy),
],
);
});
@@ -313,7 +308,7 @@ class NutritionalDiaryChartWidgetFlState extends State<NutritionalDiaryChartWidg
@override
Widget build(BuildContext context) {
final planned = widget._nutritionalPlan.plannedNutritionalValues;
final planned = widget._nutritionalPlan.nutritionalGoals;
final loggedToday = widget._nutritionalPlan.loggedNutritionalValuesToday;
final logged7DayAvg = widget._nutritionalPlan.loggedNutritionalValues7DayAvg;
@@ -322,9 +317,9 @@ class NutritionalDiaryChartWidgetFlState extends State<NutritionalDiaryChartWidg
BarChartGroupData barchartGroup(int x, double barsSpace, double barsWidth, String prop) {
final plan = planned.prop(prop);
BarChartRodData barChartRodData(double plan, double val, Color color) {
BarChartRodData barChartRodData(double? plan, double val, Color color) {
// paint a simple bar
if (plan == 0 || val == plan) {
if (plan == null || val == plan) {
return BarChartRodData(
toY: val,
color: color,

View File

@@ -31,7 +31,7 @@ class NutritionalDiaryDetailWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final valuesPlanned = _nutritionalPlan.plannedNutritionalValues;
final nutritionalGoals = _nutritionalPlan.nutritionalGoals;
final valuesLogged = _nutritionalPlan.getValuesForDate(_date);
final logs = _nutritionalPlan.getLogsForDate(_date);
@@ -52,7 +52,7 @@ class NutritionalDiaryDetailWidget extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(8.0),
child: NutritionDiaryTable(
planned: valuesPlanned,
planned: nutritionalGoals.toValues(),
logged: valuesLogged,
),
),

View File

@@ -20,8 +20,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:wger/helpers/colors.dart';
import 'package:wger/models/nutrition/nutritional_goals.dart';
import 'package:wger/models/nutrition/nutritional_plan.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';
import 'package:wger/providers/body_weight.dart';
import 'package:wger/screens/form_screen.dart';
import 'package:wger/widgets/measurements/charts.dart';
@@ -37,12 +37,11 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final plannedNutritionalValues = _nutritionalPlan.plannedNutritionalValues;
final nutritionalGoals = _nutritionalPlan.nutritionalGoals;
final lastWeightEntry =
Provider.of<BodyWeightProvider>(context, listen: false).getNewestEntry();
final valuesGperKg = lastWeightEntry != null
? _nutritionalPlan.gPerBodyKg(lastWeightEntry.weight, plannedNutritionalValues)
: null;
final nutritionalGoalsGperKg =
lastWeightEntry != null ? nutritionalGoals / lastWeightEntry.weight.toDouble() : null;
return SliverList(
delegate: SliverChildListDelegate(
@@ -82,17 +81,18 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
},
),
),
Container(
padding: const EdgeInsets.all(15),
height: 220,
child: FlNutritionalPlanPieChartWidget(plannedNutritionalValues), // chart
),
if (nutritionalGoals.isComplete())
Container(
padding: const EdgeInsets.all(15),
height: 220,
child: FlNutritionalPlanPieChartWidget(nutritionalGoals.toValues()),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: MacronutrientsTable(
plannedNutritionalValues: plannedNutritionalValues,
plannedValuesPercentage: _nutritionalPlan.energyPercentage(plannedNutritionalValues),
plannedValuesGperKg: valuesGperKg,
nutritionalGoals: nutritionalGoals,
plannedValuesPercentage: nutritionalGoals.energyPercentage(),
nutritionalGoalsGperKg: nutritionalGoalsGperKg,
),
),
const Padding(padding: EdgeInsets.all(8.0)),
@@ -104,7 +104,7 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
Container(
padding: const EdgeInsets.only(top: 15, left: 15, right: 15),
height: 300,
child: NutritionalDiaryChartWidgetFl(nutritionalPlan: _nutritionalPlan), // chart
child: NutritionalDiaryChartWidgetFl(nutritionalPlan: _nutritionalPlan),
),
Padding(
padding: const EdgeInsets.only(bottom: 40, left: 25, right: 25),
@@ -164,15 +164,15 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
class MacronutrientsTable extends StatelessWidget {
const MacronutrientsTable({
super.key,
required this.plannedNutritionalValues,
required this.nutritionalGoals,
required this.plannedValuesPercentage,
required this.plannedValuesGperKg,
required this.nutritionalGoalsGperKg,
});
static const double tablePadding = 7;
final NutritionalValues plannedNutritionalValues;
final BaseNutritionalValues plannedValuesPercentage;
final BaseNutritionalValues? plannedValuesGperKg;
final NutritionalGoals nutritionalGoals;
final NutritionalGoals plannedValuesPercentage;
final NutritionalGoals? nutritionalGoalsGperKg;
@override
Widget build(BuildContext context) {
@@ -216,8 +216,9 @@ class MacronutrientsTable extends StatelessWidget {
child: Text(AppLocalizations.of(context).energy),
),
Text(
plannedNutritionalValues.energy.toStringAsFixed(0) +
AppLocalizations.of(context).kcal,
nutritionalGoals.energy != null
? nutritionalGoals.energy!.toStringAsFixed(0) + AppLocalizations.of(context).kcal
: '',
),
const Text(''),
const Text(''),
@@ -229,11 +230,15 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding),
child: Text(AppLocalizations.of(context).protein),
),
Text(plannedNutritionalValues.protein.toStringAsFixed(0) +
AppLocalizations.of(context).g),
Text(plannedValuesPercentage.protein.toStringAsFixed(1)),
Text(
plannedValuesGperKg != null ? plannedValuesGperKg!.protein.toStringAsFixed(1) : ''),
Text(nutritionalGoals.protein != null
? nutritionalGoals.protein!.toStringAsFixed(0) + AppLocalizations.of(context).g
: ''),
Text(plannedValuesPercentage.protein != null
? plannedValuesPercentage.protein!.toStringAsFixed(1)
: ''),
Text(nutritionalGoalsGperKg != null && nutritionalGoalsGperKg!.protein != null
? nutritionalGoalsGperKg!.protein!.toStringAsFixed(1)
: ''),
],
),
TableRow(
@@ -242,11 +247,15 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding),
child: Text(AppLocalizations.of(context).carbohydrates),
),
Text(plannedNutritionalValues.carbohydrates.toStringAsFixed(0) +
AppLocalizations.of(context).g),
Text(plannedValuesPercentage.carbohydrates.toStringAsFixed(1)),
Text(plannedValuesGperKg != null
? plannedValuesGperKg!.carbohydrates.toStringAsFixed(1)
Text(nutritionalGoals.carbohydrates != null
? nutritionalGoals.carbohydrates!.toStringAsFixed(0) +
AppLocalizations.of(context).g
: ''),
Text(plannedValuesPercentage.carbohydrates != null
? plannedValuesPercentage.carbohydrates!.toStringAsFixed(1)
: ''),
Text(nutritionalGoalsGperKg != null && nutritionalGoalsGperKg!.carbohydrates != null
? nutritionalGoalsGperKg!.carbohydrates!.toStringAsFixed(1)
: ''),
],
),
@@ -256,10 +265,12 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding, horizontal: 12),
child: Text(AppLocalizations.of(context).sugars),
),
Text(plannedNutritionalValues.carbohydratesSugar.toStringAsFixed(0) +
AppLocalizations.of(context).g),
const Text(''),
const Text(''),
Text(nutritionalGoals.carbohydratesSugar != null
? nutritionalGoals.carbohydratesSugar!.toStringAsFixed(0) +
AppLocalizations.of(context).g
: ''),
],
),
TableRow(
@@ -268,9 +279,15 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding),
child: Text(AppLocalizations.of(context).fat),
),
Text(plannedNutritionalValues.fat.toStringAsFixed(0) + AppLocalizations.of(context).g),
Text(plannedValuesPercentage.fat.toStringAsFixed(1)),
Text(plannedValuesGperKg != null ? plannedValuesGperKg!.fat.toStringAsFixed(1) : ''),
Text(nutritionalGoals.fat != null
? nutritionalGoals.fat!.toStringAsFixed(0) + AppLocalizations.of(context).g
: ''),
Text(plannedValuesPercentage.fat != null
? plannedValuesPercentage.fat!.toStringAsFixed(1)
: ''),
Text(nutritionalGoalsGperKg != null && nutritionalGoalsGperKg!.fat != null
? nutritionalGoalsGperKg!.fat!.toStringAsFixed(1)
: ''),
],
),
TableRow(
@@ -279,10 +296,11 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding, horizontal: 12),
child: Text(AppLocalizations.of(context).saturatedFat),
),
Text(plannedNutritionalValues.fatSaturated.toStringAsFixed(0) +
AppLocalizations.of(context).g),
const Text(''),
const Text(''),
Text(nutritionalGoals.fatSaturated != null
? nutritionalGoals.fatSaturated!.toStringAsFixed(0) + AppLocalizations.of(context).g
: ''),
],
),
TableRow(
@@ -291,10 +309,11 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding),
child: Text(AppLocalizations.of(context).fibres),
),
Text(plannedNutritionalValues.fibres.toStringAsFixed(0) +
AppLocalizations.of(context).g),
const Text(''),
const Text(''),
Text(nutritionalGoals.fibres != null
? nutritionalGoals.fibres!.toStringAsFixed(0) + AppLocalizations.of(context).g
: ''),
],
),
TableRow(
@@ -303,10 +322,11 @@ class MacronutrientsTable extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: tablePadding),
child: Text(AppLocalizations.of(context).sodium),
),
Text(plannedNutritionalValues.sodium.toStringAsFixed(0) +
AppLocalizations.of(context).g),
const Text(''),
const Text(''),
Text(nutritionalGoals.sodium != null
? nutritionalGoals.sodium!.toStringAsFixed(0) + AppLocalizations.of(context).g
: ''),
],
),
],

View File

@@ -17,6 +17,7 @@
*/
import 'package:flutter_test/flutter_test.dart';
import 'package:wger/models/nutrition/nutritional_goals.dart';
import 'package:wger/models/nutrition/nutritional_plan.dart';
import 'package:wger/models/nutrition/nutritional_values.dart';
@@ -32,7 +33,7 @@ void main() {
group('model tests', () {
test('Test the nutritionalValues method for nutritional plans', () {
final values = NutritionalValues.values(4118.75, 32.75, 347.5, 9.5, 59.0, 37.75, 52.5, 30.5);
expect(plan.plannedNutritionalValues, values);
expect(plan.nutritionalGoals, values);
});
test('Test the nutritionalValues method for meals', () {