18
18
19
19
import 'package:flutter/material.dart' ;
20
20
import 'package:intl/intl.dart' ;
21
+ import 'package:provider/provider.dart' ;
21
22
import 'package:wger/l10n/generated/app_localizations.dart' ;
23
+ import 'package:wger/providers/body_weight.dart' ;
22
24
import 'package:wger/providers/nutrition.dart' ;
25
+ import 'package:wger/providers/user.dart' ;
23
26
import 'package:wger/screens/nutritional_plan_screen.dart' ;
24
27
import 'package:wger/widgets/core/text_prompt.dart' ;
28
+ import 'package:wger/widgets/measurements/charts.dart' ;
25
29
26
30
class NutritionalPlansList extends StatelessWidget {
27
31
final NutritionPlansProvider _nutritionProvider;
28
32
29
33
const NutritionalPlansList (this ._nutritionProvider);
30
34
35
+ /// Builds the weight change information for a nutritional plan period
36
+ Widget _buildWeightChangeInfo (
37
+ BuildContext context, DateTime startDate, DateTime ? endDate) {
38
+ final _provider = Provider .of <BodyWeightProvider >(context, listen: false );
39
+
40
+ final entriesAll = _provider.items
41
+ .map ((e) => MeasurementChartEntry (e.weight, e.date))
42
+ .toList ();
43
+ final entries7dAvg = moving7dAverage (entriesAll);
44
+ print ('start: $startDate ' );
45
+ print ('end: $endDate ' );
46
+ // Filter weight entries within the plan period
47
+ final DateTime planEndDate = endDate ?? DateTime .now ();
48
+ final List <MeasurementChartEntry > entriesInPeriod = entries7dAvg
49
+ .where ((entry) =>
50
+ entry.date.isAfter (startDate) && entry.date.isBefore (planEndDate))
51
+ .toList ();
52
+ print ('entriesInPeriod: ${entriesInPeriod .length }' );
53
+ if (entriesInPeriod.length < 2 ) {
54
+ return const SizedBox .shrink ();
55
+ }
56
+
57
+ // Sort entries by date
58
+ entriesInPeriod.sort ((a, b) => a.date.compareTo (b.date));
59
+
60
+ // Calculate weight change
61
+ final firstWeight = entriesInPeriod.first;
62
+ final lastWeight = entriesInPeriod.last;
63
+ final weightDifference = lastWeight.value - firstWeight.value;
64
+
65
+ // Format the weight change text and determine color
66
+ final String weightChangeText;
67
+ final Color weightChangeColor;
68
+ final profile = context.read <UserProvider >().profile;
69
+
70
+ final unit = weightUnit (profile! .isMetric, context);
71
+
72
+ // TODO: only proceed if it's "representative" (if we covered the plan timespan well enough), or actually,
73
+ // we could also interpolate the missing values
74
+ if (weightDifference > 0 ) {
75
+ weightChangeText = '+${weightDifference .toStringAsFixed (1 )} $unit ' ;
76
+ weightChangeColor = Colors .red;
77
+ } else if (weightDifference < 0 ) {
78
+ weightChangeText = '${weightDifference .toStringAsFixed (1 )} $unit ' ;
79
+ weightChangeColor = Colors .green;
80
+ } else {
81
+ weightChangeText = '0 $unit ' ;
82
+ weightChangeColor = Colors .grey;
83
+ }
84
+
85
+ return Padding (
86
+ padding: const EdgeInsets .only (top: 4.0 ),
87
+ child: Row (
88
+ children: [
89
+ Text (
90
+ AppLocalizations .of (context).weight + ' change: ' ,
91
+ style: Theme .of (context).textTheme.bodySmall,
92
+ ),
93
+ Text (
94
+ weightChangeText,
95
+ style: Theme .of (context).textTheme.bodySmall? .copyWith (
96
+ fontWeight: FontWeight .bold,
97
+ color: weightChangeColor,
98
+ ),
99
+ ),
100
+ ],
101
+ ),
102
+ );
103
+ }
104
+
31
105
@override
32
106
Widget build (BuildContext context) {
33
107
return RefreshIndicator (
@@ -48,16 +122,23 @@ class NutritionalPlansList extends StatelessWidget {
48
122
);
49
123
},
50
124
title: Text (currentPlan.getLabel (context)),
51
- subtitle: Text (
52
- currentPlan.endDate != null
53
- ? 'from ${DateFormat .yMd (
54
- Localizations .localeOf (context ).languageCode ,
55
- ).format (currentPlan .startDate )} to ${DateFormat .yMd (
56
- Localizations .localeOf (context ).languageCode ,
57
- ).format (currentPlan .endDate !)}'
58
- : 'from ${DateFormat .yMd (
59
- Localizations .localeOf (context ).languageCode ,
60
- ).format (currentPlan .startDate )} (open ended)' ,
125
+ subtitle: Column (
126
+ crossAxisAlignment: CrossAxisAlignment .start,
127
+ children: [
128
+ Text (
129
+ currentPlan.endDate != null
130
+ ? 'from ${DateFormat .yMd (
131
+ Localizations .localeOf (context ).languageCode ,
132
+ ).format (currentPlan .startDate )} to ${DateFormat .yMd (
133
+ Localizations .localeOf (context ).languageCode ,
134
+ ).format (currentPlan .endDate !)}'
135
+ : 'from ${DateFormat .yMd (
136
+ Localizations .localeOf (context ).languageCode ,
137
+ ).format (currentPlan .startDate )} (open ended)' ,
138
+ ),
139
+ _buildWeightChangeInfo (context, currentPlan.startDate,
140
+ currentPlan.endDate),
141
+ ],
61
142
),
62
143
trailing: Row (mainAxisSize: MainAxisSize .min, children: [
63
144
const VerticalDivider (),
0 commit comments