diet-guard/app/lib/screens/body_screen_data.dart
Krzysztof kuhy Rudnicki 2a674774bb Fit Body and Food Bank on one phone screen
Body: 2-column tiles, each opens its card in a sheet. Food bank: paged
3-column tiles, tap for macros. Pixel 6a widget test asserts no vertical
scroll on log, calendar, food bank (250 foods) and body.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWVUTaKFW8m7YrvWbzV
2026-11-02 08:08:23 +01:00

79 lines
2.7 KiB
Dart

/// Pure data helpers behind the Body screen (split out for the 250-line cap).
library;
import 'package:diet_guard_app/services/activity_kcal.dart';
import 'package:diet_guard_app/services/body_calc.dart';
import 'package:diet_guard_app/services/body_service.dart';
import 'package:diet_guard_app/services/body_steps.dart';
import 'package:diet_guard_app/services/log_storage_service.dart';
/// Builds the person from the stored profile and newest weight, or null
/// when anything the formulas need is missing.
Biometrics? currentBiometrics(BodyService body, DateTime today) {
final birth = DateTime.tryParse(body.birth ?? '');
final height = body.heightCm;
final sex = body.sex;
final weight = body.latestWeight;
if (birth == null || height == null || sex == null || weight == null) {
return null;
}
return Biometrics(
weightKg: weight.$2,
heightCm: height,
ageYears: ageOn(birth, today).toDouble(),
isMale: sex == 'm',
);
}
/// Steps outside workouts by day, as stored.
Map<String, int> stepsByDay(BodyService body) =>
body.document.steps.map((day, entry) => MapEntry(day, entry.n));
/// Mean daily exercise per formula (steps included), the steps' own share,
/// and the number of full days averaged -- like the PC's `_body_view`: the
/// two means are null without a person, or without any published workout
/// or step count.
(Map<String, double>?, double?, int) bodyExercise(
BodyService body,
Biometrics? bio,
DateTime today,
) {
final sessions = body.sessions;
final steps = stepsByDay(body);
if (bio == null || (sessions == null && steps.isEmpty)) {
return (null, null, 0);
}
final byDay = steps.map((day, n) => MapEntry(day, stepKcal(n, bio)));
final weight = bio.weightKg;
final all = sessions ?? const <Session>[];
return (
dailyExercise(all, weight, today, byDay),
extraShare(all, today, byDay),
windowDays(all, today, byDay),
);
}
/// Sums each day's non-deleted kcal.
Map<String, double> kcalByDay(DayLog log) => {
for (final entry in log.entries)
entry.key: entry.value
.where((food) => !food.deleted)
.fold<double>(0, (sum, food) => sum + food.kcal),
};
/// Why the numbers are missing.
String bodyMissingNote(BodyService body) {
if (body.latestWeight == null && body.birth == null) {
return 'Set your profile and enter your weight to see BMI, ideal '
'weight and calorie targets.';
}
if (body.latestWeight == null) return 'Enter your weight to see the numbers.';
return 'Set your profile (birth date, height, sex) to see the numbers.';
}
/// The age line value.
String bodyAgeText(BodyService body, DateTime today) {
final birth = DateTime.tryParse(body.birth ?? '');
return birth == null ? 'unknown' : '${ageOn(birth, today)}';
}