Files
flutter/lib/widgets/measurements/helpers.dart
Roland Geider 98f2fb0096 Merge branch 'master' into powersync
# Conflicts:
#	Gemfile.lock
#	android/settings.gradle
#	fastlane/report.xml
#	integration_test/1_dashboard.dart
#	integration_test/2_workout.dart
#	integration_test/3_gym_mode.dart
#	integration_test/5_nutritional_plan.dart
#	integration_test/6_weight.dart
#	lib/helpers/i18n.dart
#	lib/helpers/ui.dart
#	lib/main.dart
#	lib/models/nutrition/meal.g.dart
#	lib/models/nutrition/nutritional_plan.dart
#	lib/providers/auth.dart
#	lib/providers/nutrition.dart
#	lib/screens/add_exercise_screen.dart
#	lib/screens/auth_screen.dart
#	lib/screens/dashboard.dart
#	lib/screens/exercises_screen.dart
#	lib/screens/home_tabs_screen.dart
#	lib/screens/log_meal_screen.dart
#	lib/screens/log_meals_screen.dart
#	lib/screens/nutritional_plan_screen.dart
#	lib/screens/routine_list_screen.dart
#	lib/screens/workout_plan_screen.dart
#	lib/widgets/add_exercise/steps/step1basics.dart
#	lib/widgets/add_exercise/steps/step5images.dart
#	lib/widgets/add_exercise/steps/step_2_variations.dart
#	lib/widgets/add_exercise/steps/step_3_description.dart
#	lib/widgets/add_exercise/steps/step_4_translations.dart
#	lib/widgets/core/about.dart
#	lib/widgets/core/settings.dart
#	lib/widgets/dashboard/calendar.dart
#	lib/widgets/dashboard/widgets.dart
#	lib/widgets/exercises/exercises.dart
#	lib/widgets/gallery/overview.dart
#	lib/widgets/measurements/categories_card.dart
#	lib/widgets/measurements/charts.dart
#	lib/widgets/measurements/entries.dart
#	lib/widgets/measurements/forms.dart
#	lib/widgets/measurements/helpers.dart
#	lib/widgets/nutrition/charts.dart
#	lib/widgets/nutrition/forms.dart
#	lib/widgets/nutrition/helpers.dart
#	lib/widgets/nutrition/meal.dart
#	lib/widgets/nutrition/nutritional_diary_table.dart
#	lib/widgets/nutrition/nutritional_plan_detail.dart
#	lib/widgets/nutrition/nutritional_plans_list.dart
#	lib/widgets/nutrition/widgets.dart
#	lib/widgets/routines/charts.dart
#	lib/widgets/routines/workout_logs.dart
#	lib/widgets/weight/forms.dart
#	lib/widgets/weight/weight_overview.dart
#	lib/widgets/workouts/app_bar.dart
#	lib/widgets/workouts/day.dart
#	lib/widgets/workouts/forms.dart
#	lib/widgets/workouts/gym_mode.dart
#	lib/widgets/workouts/workout_plan_detail.dart
#	lib/widgets/workouts/workout_plans_list.dart
#	linux/flutter/generated_plugin_registrant.cc
#	linux/flutter/generated_plugins.cmake
#	pubspec.lock
#	pubspec.yaml
#	test/auth/auth_screen_test.dart
#	test/core/settings_test.dart
#	test/core/settings_test.mocks.dart
#	test/nutrition/nutritional_meal_form_test.mocks.dart
#	test/nutrition/nutritional_meal_item_form_test.dart
#	test/nutrition/nutritional_plan_form_test.mocks.dart
#	test/nutrition/nutritional_plan_screen_test.dart
#	test/nutrition/nutritional_plans_screen_test.dart
#	test/routine/repetition_unit_form_widget_test.dart
#	test/routine/routine_screen_test.dart
#	test/routine/routines_screen_test.dart
#	test/routine/weight_unit_form_widget_test.dart
#	test/workout/gym_mode_screen_test.dart
#	test/workout/workout_day_form_test.dart
#	test/workout/workout_form_test.dart
#	test/workout/workout_set_form_test.dart
2025-10-19 14:48:15 +02:00

119 lines
4.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:wger/helpers/measurements.dart';
import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/nutrition/nutritional_plan.dart';
import 'package:wger/widgets/measurements/charts.dart';
List<Widget> getOverviewWidgets(
String title,
List<MeasurementChartEntry> raw,
List<MeasurementChartEntry> avg,
String unit,
BuildContext context,
) {
return [
Text(title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge),
Container(
padding: const EdgeInsets.all(15),
height: 220,
child: raw.isEmpty
? Center(
child: Text(
'No data available',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Theme.of(context).colorScheme.secondary.withValues(alpha: 0.7),
),
),
)
: MeasurementChartWidgetFl(raw, unit, avgs: avg),
),
if (avg.isNotEmpty) MeasurementOverallChangeWidget(avg.first, avg.last, unit),
const SizedBox(height: 8),
];
}
List<Widget> getOverviewWidgetsSeries(
String name,
List<MeasurementChartEntry> entriesAll,
List<MeasurementChartEntry> entries7dAvg,
List<NutritionalPlan> plans,
String unit,
BuildContext context,
) {
final monthAgo = DateTime.now().subtract(const Duration(days: 30));
return [
...getOverviewWidgets(
AppLocalizations.of(context).chartAllTimeTitle(name),
entriesAll,
entries7dAvg,
unit,
context,
),
// Show overview widgets for each plan in plans
for (final plan in plans)
...getOverviewWidgets(
AppLocalizations.of(context).chartDuringPlanTitle(name, plan.description),
entriesAll.whereDateWithInterpolation(plan.startDate, plan.endDate),
entries7dAvg.whereDateWithInterpolation(plan.startDate, plan.endDate),
unit,
context,
),
// if all time is significantly longer than 30 days (let's say > 75 days)
// then let's show a separate chart just focusing on the last 30 days,
// if there is data for it.
if (entriesAll.isNotEmpty &&
entriesAll.first.date.isBefore(entriesAll.last.date.subtract(const Duration(days: 75))) &&
entriesAll.any((e) => e.date.isAfter(monthAgo)))
...getOverviewWidgets(
AppLocalizations.of(context).chart30DaysTitle(name),
entriesAll.whereDateWithInterpolation(monthAgo, null),
entries7dAvg.whereDateWithInterpolation(monthAgo, null),
unit,
context,
),
// legend
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Indicator(
color: Theme.of(context).colorScheme.primary,
text: AppLocalizations.of(context).indicatorRaw,
isSquare: true,
),
Indicator(
color: Theme.of(context).colorScheme.tertiary,
text: AppLocalizations.of(context).indicatorAvg,
isSquare: true,
),
],
),
];
}
// return the raw and average measurements for a "sensible range"
// a sensible range is something relatively recent, which is most relevant
// for the user to track their progress, but a range should always include
// at least 5 points, and if not we chose a bigger one.
// we return a range of the last 2 months, 4 months, or the full history
(List<MeasurementChartEntry>, List<MeasurementChartEntry>) sensibleRange(
List<MeasurementChartEntry> entriesAll,
) {
final entries7dAvg = moving7dAverage(entriesAll);
final twoMonthsAgo = DateTime.now().subtract(const Duration(days: 61));
final fourMonthsAgo = DateTime.now().subtract(const Duration(days: 122));
if (entriesAll.whereDate(twoMonthsAgo, null).length > 4) {
return (
entriesAll.whereDate(twoMonthsAgo, null),
entries7dAvg.whereDate(twoMonthsAgo, null),
);
}
if (entriesAll.whereDate(fourMonthsAgo, null).length > 4) {
return (
entriesAll.whereDate(fourMonthsAgo, null),
entries7dAvg.whereDate(fourMonthsAgo, null),
);
}
return (entriesAll, entries7dAvg);
}