Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
/// A row of macro entry fields (kcal/protein/carbs/fat/grams), with an
|
|
|
|
|
/// optional reference weight so a label's per-100g macros can be typed
|
|
|
|
|
/// directly and scaled to the amount actually eaten.
|
|
|
|
|
library;
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
|
|
|
|
/// Text controllers for one macro-entry row, owned by the calling screen so
|
|
|
|
|
/// it can read/clear/prefill values around the row's lifecycle.
|
|
|
|
|
class MacroControllers {
|
|
|
|
|
/// Creates a fresh set of empty macro controllers.
|
|
|
|
|
MacroControllers()
|
|
|
|
|
: kcal = TextEditingController(),
|
|
|
|
|
protein = TextEditingController(),
|
|
|
|
|
carbs = TextEditingController(),
|
|
|
|
|
fat = TextEditingController(),
|
|
|
|
|
perGrams = TextEditingController(),
|
|
|
|
|
grams = TextEditingController();
|
|
|
|
|
|
|
|
|
|
/// Calories controller.
|
|
|
|
|
final TextEditingController kcal;
|
|
|
|
|
|
|
|
|
|
/// Protein (g) controller.
|
|
|
|
|
final TextEditingController protein;
|
|
|
|
|
|
|
|
|
|
/// Carbohydrate (g) controller.
|
|
|
|
|
final TextEditingController carbs;
|
|
|
|
|
|
|
|
|
|
/// Fat (g) controller.
|
|
|
|
|
final TextEditingController fat;
|
|
|
|
|
|
|
|
|
|
/// Reference weight (g) the typed macros are stated for, e.g. `100` for
|
|
|
|
|
/// a per-100g label. Blank means the macros already describe the full
|
|
|
|
|
/// eaten portion.
|
|
|
|
|
final TextEditingController perGrams;
|
|
|
|
|
|
|
|
|
|
/// Portion weight actually eaten (g). Blank assumes the eaten amount
|
|
|
|
|
/// equals [perGrams].
|
|
|
|
|
final TextEditingController grams;
|
|
|
|
|
|
|
|
|
|
/// Clears every field's text.
|
|
|
|
|
void clear() {
|
|
|
|
|
kcal.clear();
|
|
|
|
|
protein.clear();
|
|
|
|
|
carbs.clear();
|
|
|
|
|
fat.clear();
|
|
|
|
|
perGrams.clear();
|
|
|
|
|
grams.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Disposes every controller.
|
|
|
|
|
void dispose() {
|
|
|
|
|
kcal.dispose();
|
|
|
|
|
protein.dispose();
|
|
|
|
|
carbs.dispose();
|
|
|
|
|
fat.dispose();
|
|
|
|
|
perGrams.dispose();
|
|
|
|
|
grams.dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A labeled row of number-entry fields for calories, macros, and the
|
|
|
|
|
/// optional reference-weight-vs-eaten-weight split.
|
2026-07-04 05:19:23 +02:00
|
|
|
///
|
|
|
|
|
/// Layout mirrors the Python gate's macro section: the reference weight
|
|
|
|
|
/// (`per (g)`) sits on the same line as `kcal` so the user can see at a
|
|
|
|
|
/// glance which portion size the calories describe.
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
class MacroInputRow extends StatelessWidget {
|
|
|
|
|
/// Creates a [MacroInputRow] bound to [controllers].
|
2026-07-04 05:19:23 +02:00
|
|
|
///
|
|
|
|
|
/// When [compact] is true, all six fields render in a single row with
|
|
|
|
|
/// abbreviated labels instead of the default three stacked rows.
|
|
|
|
|
const MacroInputRow({
|
|
|
|
|
required this.controllers,
|
|
|
|
|
this.compact = false,
|
|
|
|
|
super.key,
|
|
|
|
|
});
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
|
|
|
|
|
/// The text controllers this row reads from and writes to.
|
|
|
|
|
final MacroControllers controllers;
|
|
|
|
|
|
2026-07-04 05:19:23 +02:00
|
|
|
/// Whether to render all fields in one row with abbreviated labels.
|
|
|
|
|
final bool compact;
|
|
|
|
|
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
2026-07-04 05:19:23 +02:00
|
|
|
if (compact) {
|
|
|
|
|
return Row(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(child: _macroField('per', controllers.perGrams)),
|
|
|
|
|
const SizedBox(width: 4),
|
|
|
|
|
Expanded(child: _macroField('kcal', controllers.kcal)),
|
|
|
|
|
const SizedBox(width: 4),
|
|
|
|
|
Expanded(child: _macroField('P', controllers.protein)),
|
|
|
|
|
const SizedBox(width: 4),
|
|
|
|
|
Expanded(child: _macroField('C', controllers.carbs)),
|
|
|
|
|
const SizedBox(width: 4),
|
|
|
|
|
Expanded(child: _macroField('F', controllers.fat)),
|
|
|
|
|
const SizedBox(width: 4),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Tooltip(
|
|
|
|
|
message: "blank = same as 'per (g)'",
|
|
|
|
|
child: _macroField('eaten', controllers.grams),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
return Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
2026-07-04 05:19:23 +02:00
|
|
|
// per-gram reference weight and kcal on the same line.
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
Row(
|
2026-07-04 05:19:23 +02:00
|
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
children: [
|
2026-07-04 05:19:23 +02:00
|
|
|
SizedBox(
|
|
|
|
|
width: 72,
|
|
|
|
|
child: _macroField('per (g)', controllers.perGrams),
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
),
|
2026-07-04 05:19:23 +02:00
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
Expanded(child: _macroField('kcal', controllers.kcal)),
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(child: _macroField('protein g', controllers.protein)),
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
Expanded(child: _macroField('carbs g', controllers.carbs)),
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
Expanded(child: _macroField('fat g', controllers.fat)),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
_macroField(
|
2026-07-04 05:19:23 +02:00
|
|
|
'eaten (g)',
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
controllers.grams,
|
2026-07-04 05:19:23 +02:00
|
|
|
helperText: "blank = same as 'per (g)'",
|
Add Flutter companion app skeleton with local meal logging
Milestone 1 of the diet-app-as-wise-balloon plan: a phone-native way to
log meals away from the PC, sharing the exact on-disk JSON shape
diet_guard already uses (same field names, no translation layer).
- lib/models/: 1:1 Dart mirrors of the Python dataclasses (Nutrition,
FoodEntry, MealItem, FoodBankRecord, Slot), including the per-100g/
amount-eaten portion scaling that matches _resolve.resolve_nutrition's
semantics exactly.
- lib/services/log_storage_service.dart: plain-JSON persistence to
food_log.json's exact shape (no sqflite -- the canonical format
already is this JSON).
- lib/services/foodbank_service.dart: ports _foodbank.py's upsert/fuzzy
search logic for autocomplete.
- lib/screens/: log_meal_screen.dart (single-item logging) and
meal_builder_screen.dart (composite multi-item meals, logging full
per-component macros via the new components field).
Verified end-to-end on a physical device (BL9000): built, installed,
logged a real meal through the UI. 77 Flutter tests passing, `flutter
analyze` clean against very_good_analysis.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU3f5KQ1GHXsbbSecfVEyF
2026-06-22 18:22:42 +02:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _macroField(
|
|
|
|
|
String label,
|
|
|
|
|
TextEditingController controller, {
|
|
|
|
|
String? helperText,
|
|
|
|
|
}) {
|
|
|
|
|
return TextField(
|
|
|
|
|
controller: controller,
|
|
|
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
|
labelText: label,
|
|
|
|
|
helperText: helperText,
|
|
|
|
|
isDense: true,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|