display weight change per NP on the NP list page

This commit is contained in:
Dieter Plaetinck
2025-06-27 22:46:09 +02:00
parent d1cfce7526
commit 56adac2ba3

View File

@@ -18,16 +18,90 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/body_weight.dart';
import 'package:wger/providers/nutrition.dart';
import 'package:wger/providers/user.dart';
import 'package:wger/screens/nutritional_plan_screen.dart';
import 'package:wger/widgets/core/text_prompt.dart';
import 'package:wger/widgets/measurements/charts.dart';
class NutritionalPlansList extends StatelessWidget {
final NutritionPlansProvider _nutritionProvider;
const NutritionalPlansList(this._nutritionProvider);
/// Builds the weight change information for a nutritional plan period
Widget _buildWeightChangeInfo(
BuildContext context, DateTime startDate, DateTime? endDate) {
final _provider = Provider.of<BodyWeightProvider>(context, listen: false);
final entriesAll = _provider.items
.map((e) => MeasurementChartEntry(e.weight, e.date))
.toList();
final entries7dAvg = moving7dAverage(entriesAll);
print('start: $startDate');
print('end: $endDate');
// Filter weight entries within the plan period
final DateTime planEndDate = endDate ?? DateTime.now();
final List<MeasurementChartEntry> entriesInPeriod = entries7dAvg
.where((entry) =>
entry.date.isAfter(startDate) && entry.date.isBefore(planEndDate))
.toList();
print('entriesInPeriod: ${entriesInPeriod.length}');
if (entriesInPeriod.length < 2) {
return const SizedBox.shrink();
}
// Sort entries by date
entriesInPeriod.sort((a, b) => a.date.compareTo(b.date));
// Calculate weight change
final firstWeight = entriesInPeriod.first;
final lastWeight = entriesInPeriod.last;
final weightDifference = lastWeight.value - firstWeight.value;
// Format the weight change text and determine color
final String weightChangeText;
final Color weightChangeColor;
final profile = context.read<UserProvider>().profile;
final unit = weightUnit(profile!.isMetric, context);
// TODO: only proceed if it's "representative" (if we covered the plan timespan well enough), or actually,
// we could also interpolate the missing values
if (weightDifference > 0) {
weightChangeText = '+${weightDifference.toStringAsFixed(1)} $unit';
weightChangeColor = Colors.red;
} else if (weightDifference < 0) {
weightChangeText = '${weightDifference.toStringAsFixed(1)} $unit';
weightChangeColor = Colors.green;
} else {
weightChangeText = '0 $unit';
weightChangeColor = Colors.grey;
}
return Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Row(
children: [
Text(
AppLocalizations.of(context).weight + ' change: ',
style: Theme.of(context).textTheme.bodySmall,
),
Text(
weightChangeText,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontWeight: FontWeight.bold,
color: weightChangeColor,
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return RefreshIndicator(
@@ -48,16 +122,23 @@ class NutritionalPlansList extends StatelessWidget {
);
},
title: Text(currentPlan.getLabel(context)),
subtitle: Text(
currentPlan.endDate != null
? 'from ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.startDate)} to ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.endDate!)}'
: 'from ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.startDate)} (open ended)',
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
currentPlan.endDate != null
? 'from ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.startDate)} to ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.endDate!)}'
: 'from ${DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.startDate)} (open ended)',
),
_buildWeightChangeInfo(context, currentPlan.startDate,
currentPlan.endDate),
],
),
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
const VerticalDivider(),