Skip to content

feat(meal-detail): quick-serving chips (0.5x/1x/2x + 100 g) - #1101

Open
ibrahim-iqbal wants to merge 1 commit into
simonoppowa:developfrom
ibrahim-iqbal:feat/quick-serving-chips-577
Open

feat(meal-detail): quick-serving chips (0.5x/1x/2x + 100 g)#1101
ibrahim-iqbal wants to merge 1 commit into
simonoppowa:developfrom
ibrahim-iqbal:feat/quick-serving-chips-577

Conversation

@ibrahim-iqbal

Copy link
Copy Markdown
Contributor

Fixes part of #577 — the quick-quantity chip row on the meal-detail sheet was hard-coded to the 50/100/150/200/250 g presets, which are meaningless for foods measured by serving (a slice of pizza, a scoop of protein) and impossible to hit for liquids.

Changes

New helper — lib/features/meal_detail/util/quick_serving_option.dart

Builds the chip row from the food itself:

  • 0.5×, 1×, 2× — when the meal exposes a scalableServingQuantity (either an explicit servingQuantity or one recovered from the servingSize string). Selecting a chip sets the quantity + switches the unit to serving, so nutriment math goes through the same path as the existing dropdown.
  • 100 g — when the food is measured by mass (isSolid). Uses the localised gram-unit label passed in from S.of(context).gramUnit.
  • No row at all — when the food has neither a serving nor a mass unit. Better than showing a chip that rounds to zero.

Sheet wiring — meal_detail_bottom_sheet.dart

Replaces the hard-coded chip loop with a Builder that consults quickServingOptionsFor and renders each option as an ActionChip wrapped in a Semantics(identifier: 'meal-detail-chip-<label>') so a11y and integration tests can address individual chips. The chip's onPressed writes both the quantity text and the new unit through the existing onQuantityOrUnitChanged callback, which is what the dropdown and manual typing already fan out through.

Quantity is rendered with a small _formatQuantity helper so 1.0 stays 1 and 0.5 renders as 0.5 — the input field's regex allows both shapes but users expect the shorter one from a chip.

Test — test/unit_test/quick_serving_option_test.dart

Six cases against quickServingOptionsFor:

  • solid + scalable serving → 4 chips (0.5×, , , 100 g)
  • liquid + scalable serving → 3 chips (multipliers only)
  • solid without a serving → single 100 g chip
  • neither serving nor mass → empty row
  • gram-unit label passthrough (used to lock in the localisation entry point)
  • scalableServingQuantity recovering from a 1 slice (30 g) string when servingQuantity is null

Acceptance criteria mapping

  • Show 0.5×, 1×, 2× when a valid serving size is available
  • Include a 100 g choice for foods measured by mass
  • Selecting a chip updates the existing quantity/unit controls and nutrition preview
  • Manual quantity and unit entry continues to work
  • Do not show invalid options for foods without the required serving metadata
  • Preserve locale-aware unit labels (gram unit routed through S.of(context).gramUnit)
  • Add stable Semantics(identifier: ...) values to each interactive chip
  • Unit tests for selection mapping, presence, and missing-serving behaviour

Remembering the last-used portion stays out of scope per the issue.

Test plan

  • flutter test test/unit_test/quick_serving_option_test.dart — 6/6 pass locally
  • dart format clean on both changed files
  • Manual check on Android
  • Manual check on iOS

Replaces the fixed 50-250 g quick-quantity chip row on the meal-detail
sheet with chips derived from the food itself: 0.5x, 1x, 2x when the
product carries a scalable serving, and a 100 g shortcut when the food
is measured by mass. Foods with neither hide the row entirely instead
of showing a chip that rounds to nothing meaningful.

Each chip writes both the quantity and the corresponding unit through
the existing onQuantityOrUnitChanged callback, so the nutriment preview
recalculates through the same path a dropdown change would take.
Manual entry keeps working — the callback already fires on text change.

The chip data is built by quickServingOptionsFor in a new util file so
the mapping is unit-testable without spinning up the sheet's widget
tree, and covered by a matching test.

Fixes part of simonoppowa#577
@simonoppowa

Copy link
Copy Markdown
Owner

Thanks — this is the right shape for #577 and the helper reads cleanly. I checked it out, merged develop, and drove the real meal-detail screen: chips set the text, switch the unit, recalculate kcal, and the logged intake carries the converted amount — all correct. Two small asks before I merge: (1) meal_detail_bottom_sheet.dart:211 interpolates the visible label into the Semantics identifier, so on the uk locale the 100 g chip becomes meal-detail-chip-100 г and the others carry ×/spaces; give QuickServingOption a stable ASCII id (e.g. half-serving, one-serving, double-serving, 100g) and interpolate that. (2) The Navigator.of(context).popUntil(...) hunk at 352-353 is formatter drift between SDK versions — could you put that one back to its develop shape? The other two reformatted lines are fine. Optional, not blocking: isSolid for the 100 g chip is narrower than the dropdown's gate at 167-170, so quick-add custom meals (gml) and liquids without a serving now get no row where they had one; mirroring the dropdown gate would close that. And if you have the appetite, a widget test that taps a chip on MealDetailScreen would cover what the helper tests can't — the edit_meal_unwind_test harness has almost every fake you need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants