fix: show dish calories in search and fix portion sheet layout crash

- DishSearchResult now carries calories_per_serving (backend entity + repo
  LEFT JOIN recipes / MIN / GROUP BY; Flutter model + fromJson)
- _FoodTile.fromDish shows kcal/serving subtitle when available
- _DishPortionSheet quick-select buttons: Row → Wrap to avoid
  BoxConstraints infinite-width crash inside DraggableScrollableSheet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-21 16:08:20 +02:00
parent 6af7d2fade
commit bf8dce36c5
4 changed files with 31 additions and 23 deletions

View File

@@ -7,12 +7,14 @@ class DishSearchResult {
final String name;
final String? imageUrl;
final double avgRating;
final double? caloriesPerServing;
const DishSearchResult({
required this.id,
required this.name,
this.imageUrl,
required this.avgRating,
this.caloriesPerServing,
});
factory DishSearchResult.fromJson(Map<String, dynamic> json) {
@@ -21,6 +23,8 @@ class DishSearchResult {
name: json['name'] as String,
imageUrl: json['image_url'] as String?,
avgRating: (json['avg_rating'] as num?)?.toDouble() ?? 0,
caloriesPerServing:
(json['calories_per_serving'] as num?)?.toDouble(),
);
}
}

View File

@@ -484,7 +484,9 @@ class _FoodTile extends StatelessWidget {
size: 20, color: Colors.green),
),
title: dish.name,
subtitle: null,
subtitle: dish.caloriesPerServing != null
? '${dish.caloriesPerServing!.toInt()} kcal / serving'
: null,
onTap: onTap,
);
}
@@ -635,27 +637,25 @@ class _DishPortionSheetState extends ConsumerState<_DishPortionSheet> {
const SizedBox(height: 16),
// Quick-select portion buttons
Row(
Wrap(
spacing: 8,
children: [
for (final quickValue in [0.5, 1.0, 1.5, 2.0])
Padding(
padding: const EdgeInsets.only(right: 8),
child: OutlinedButton(
onPressed: () => _setPortions(quickValue),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
side: BorderSide(
color: _selectedPortions == quickValue
? theme.colorScheme.primary
: theme.colorScheme.outline,
width: _selectedPortions == quickValue ? 2 : 1,
),
OutlinedButton(
onPressed: () => _setPortions(quickValue),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
side: BorderSide(
color: _selectedPortions == quickValue
? theme.colorScheme.primary
: theme.colorScheme.outline,
width: _selectedPortions == quickValue ? 2 : 1,
),
child: Text(quickValue % 1 == 0
? quickValue.toInt().toString()
: quickValue.toStringAsFixed(1)),
),
child: Text(quickValue % 1 == 0
? quickValue.toInt().toString()
: quickValue.toStringAsFixed(1)),
),
],
),