mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
nutrition plans list: show weekly delta and total duration, consistent styling
This commit is contained in:
@@ -30,6 +30,51 @@ List<DateTime> daysInRange(DateTime first, DateTime last) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Formats a Duration into a human-readable string with years, months, and days
|
||||
String humanDuration(DateTime startDate, DateTime endDate) {
|
||||
var years = endDate.year - startDate.year;
|
||||
var months = endDate.month - startDate.month;
|
||||
var days = endDate.day - startDate.day;
|
||||
|
||||
if (months < 0) {
|
||||
months += 12;
|
||||
years -= 1;
|
||||
}
|
||||
|
||||
// if we overcounted the days, it's a bit trickier to solve than overcounting months
|
||||
// e.g. consider a start date february 10 and end date june 8
|
||||
// -> days = -2
|
||||
// -> months = 4
|
||||
// the proper answer can be thought of in 3 ways:
|
||||
// * count whole months first, then days: from febr 10 to may 10, and then to june 8. that's 3 months + (num_days_in_may - 10 + 8 days)
|
||||
// * count days first, then whole months: from febr 10 to march 8, and then to june 8. that's (num_days_in_feb - 10 + 8 days) + 3 months
|
||||
// * technically, one can also use any month to adjust the day: e.g.
|
||||
// count months from febr 10 to april 10, count days to may 8, and count months again to june 8.
|
||||
// probably no-one uses the last method. The first approach seems most natural.
|
||||
// it means we need to know the number of days (aka the last day index) of the month before endDate
|
||||
// which we can do by setting the day to 0 and asking for the day
|
||||
if (days < 0) {
|
||||
days += DateTime(endDate.year, endDate.month, 0).day;
|
||||
months -= 1;
|
||||
}
|
||||
|
||||
final parts = <String>[];
|
||||
if (years > 0) {
|
||||
parts.add('$years year${years == 1 ? '' : 's'}');
|
||||
}
|
||||
if (months > 0) {
|
||||
parts.add('$months month${months == 1 ? '' : 's'}');
|
||||
}
|
||||
if (days > 0) {
|
||||
parts.add('$days day${days == 1 ? '' : 's'}');
|
||||
}
|
||||
|
||||
if (parts.isEmpty) {
|
||||
return 'Duration: 0 days';
|
||||
}
|
||||
return 'Duration: ${parts.join(', ')}';
|
||||
}
|
||||
|
||||
extension DateTimeExtension on DateTime {
|
||||
bool isSameDayAs(DateTime other) {
|
||||
final thisDay = DateTime(year, month, day);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/helpers/date.dart';
|
||||
import 'package:wger/helpers/measurements.dart';
|
||||
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/providers/body_weight.dart';
|
||||
@@ -48,8 +49,14 @@ class NutritionalPlansList extends StatelessWidget {
|
||||
final lastWeight = entries7dAvg.last;
|
||||
final weightDifference = lastWeight.value - firstWeight.value;
|
||||
|
||||
// Calculate the time period in weeks
|
||||
final timeDifference = lastWeight.date.difference(firstWeight.date);
|
||||
final weeklyRate =
|
||||
weightDifference / (timeDifference.inDays == 0 ? 1 : timeDifference.inDays) * 7;
|
||||
|
||||
// Format the weight change text and determine color
|
||||
final String weightChangeText;
|
||||
final String weeklyRateText;
|
||||
final Color weightChangeColor;
|
||||
final profile = context.read<UserProvider>().profile;
|
||||
|
||||
@@ -57,12 +64,15 @@ class NutritionalPlansList extends StatelessWidget {
|
||||
|
||||
if (weightDifference > 0) {
|
||||
weightChangeText = '+${weightDifference.toStringAsFixed(1)} $unit';
|
||||
weeklyRateText = '+${weeklyRate.toStringAsFixed(2)} $unit';
|
||||
weightChangeColor = Colors.red;
|
||||
} else if (weightDifference < 0) {
|
||||
weightChangeText = '${weightDifference.toStringAsFixed(1)} $unit';
|
||||
weeklyRateText = '${weeklyRate.toStringAsFixed(2)} $unit';
|
||||
weightChangeColor = Colors.green;
|
||||
} else {
|
||||
weightChangeText = '0 $unit';
|
||||
weeklyRateText = '0 $unit';
|
||||
weightChangeColor = Colors.grey;
|
||||
}
|
||||
|
||||
@@ -71,15 +81,11 @@ class NutritionalPlansList extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${AppLocalizations.of(context).weight} change: ',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
'${AppLocalizations.of(context).weight}: ',
|
||||
),
|
||||
Text(
|
||||
weightChangeText,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: weightChangeColor,
|
||||
),
|
||||
'$weightChangeText ($weeklyRateText/week)',
|
||||
style: TextStyle(color: weightChangeColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -109,14 +115,18 @@ class NutritionalPlansList extends StatelessWidget {
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
humanDuration(
|
||||
currentPlan.startDate, currentPlan.endDate ?? DateTime.now()),
|
||||
),
|
||||
Text(
|
||||
currentPlan.endDate != null
|
||||
? 'from ${DateFormat.yMd(
|
||||
? 'From: ${DateFormat.yMd(
|
||||
Localizations.localeOf(context).languageCode,
|
||||
).format(currentPlan.startDate)} to ${DateFormat.yMd(
|
||||
).format(currentPlan.startDate)} To: ${DateFormat.yMd(
|
||||
Localizations.localeOf(context).languageCode,
|
||||
).format(currentPlan.endDate!)}'
|
||||
: 'from ${DateFormat.yMd(
|
||||
: 'From: ${DateFormat.yMd(
|
||||
Localizations.localeOf(context).languageCode,
|
||||
).format(currentPlan.startDate)} (open ended)',
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user