Skip to content

Add pickup point selection to checkout for pickup_point delivery#425

Open
CherryCIC wants to merge 1 commit into
mainfrom
cherrypickuppointselection
Open

Add pickup point selection to checkout for pickup_point delivery#425
CherryCIC wants to merge 1 commit into
mainfrom
cherrypickuppointselection

Conversation

@CherryCIC
Copy link
Copy Markdown
Collaborator

@CherryCIC CherryCIC commented May 19, 2026

Summary

  • Added pickup point lookup against GET /api/shipping/pickup-points using checkout address data and the documented default radius.
  • Introduced a typed PickupPoint model and updated checkout state to store the selected pickup point alongside the delivery method.
  • Reworked the checkout delivery UI to show a pickup-point selection flow, with loading, empty, error, and selected states.
  • Added validation so checkout cannot proceed on pickup_point delivery until a pickup point is selected.
  • Cleared stale pickup point state when the delivery method changes away from pickup_point.
  • Updated checkout tests to cover API parsing, request parameters, validation, and state clearing.

Testing

  • flutter test test/checkout_view_model_test.dart passed.
  • dart analyze lib/features/checkout test/checkout_view_model_test.dart passed with only existing deprecation infos in unrelated checkout payment widgets.
  • flutter analyze still reports unrelated pre-existing test mock issues in test/auth_ui_test.dart, outside this change.

Notes

  • This is a frontend and checkout-state change only. The backend endpoint was assumed to exist and was not modified.
  • The implementation follows the documented pickup-point response shape, but it does not broaden the wider Sendcloud order payload beyond the selected pickup point data required here.
  • lib/features/checkout/widgets/share_location_dialog.dart was removed because the new flow no longer uses the old postcode prompt.

The frontend can now select and submit a pick-up point, but the final order payload may still fail backend shipment creation because required Sendcloud/payment fields may need work.

Summary by CodeRabbit

  • New Features

    • Added pickup point delivery option during checkout, allowing users to search and select preferred pickup locations as an alternative to home delivery.
  • Improvements

    • Enhanced checkout delivery selection workflow with improved validation logic.
    • Updated user-facing messaging for pickup point-related features.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

Replace InPost locker-based delivery with generic pickup-point support across data models, repository, view model state and fetching, order generation, UI widgets, and tests. Adds PickupPoint model with JSON deserialization, delivery choice constants, repository fetchPickupPoints method, async pickup-point fetching with address-based lookup and status management, and refactored delivery UI using a bottom-sheet picker.

Changes

Checkout Pickup Point Migration

Layer / File(s) Summary
Data Models and Configuration
lib/features/checkout/models/pickup_point.dart, lib/features/checkout/constants/address_constants.dart, lib/core/services/network/api_endpoints.dart, lib/core/config/app_strings.dart
New PickupPoint model with required address/identity fields, optional carrier/location/distance, and availability flags; JSON deserialization with multiple key aliases; derived displayAddress and distanceLabel helpers. AddressConstants.pickupPointSearchRadiusMeters set to 5000. New API endpoint /shipping/pickup-points. New and updated UI strings for pickup-point flow.
Repository fetchPickupPoints API
lib/features/checkout/checkout_repository.dart
Added fetchPickupPoints method to ICheckoutRepository interface and implementation in CheckoutRepository; performs HTTP GET with country, address, and radius query parameters; returns Result.success(response) or Result.failure(message) on error/exception.
Delivery Choice and Selection State
lib/features/checkout/checkout_view_model.dart
Added pickupPointDeliveryChoice and homeDeliveryChoice constants; new state for _pickupPoints list and selectedPickupPoint; setters setDeliveryChoice, setSelectedPickupPoint, clearSelectedPickupPoint that reset pickup data when switching away from pickup mode; updated canCheckout to require hasValidDeliverySelection && hasPaymentMethod; clearShippingAddress now also clears pickup state.
Pickup Point Fetching and Order Payload Generation
lib/features/checkout/checkout_view_model.dart
Added fetchPickupPointsForShippingAddress and fetchPickupPoints methods with async status management; _parsePickupPointList extracts pickup points from API response; processCheckout, storeOrderInFirestore, and createOrder updated to include delivery_method and conditionally include pickupPoint in order payloads; pickup-delivery address construction uses selected PickupPoint fields.
UI Widget Refactoring for Delivery Selection
lib/features/checkout/widgets/delivery_options.dart, lib/features/checkout/widgets/shipping_list_item.dart, lib/features/checkout/checkout_page.dart
DeliveryOptions refactored to drive state from CheckoutViewModel; new _selectPickupDelivery async helper validates address, fetches pickup points, and opens PickupPointPickerSheet; added _PickupPointSelectionSection and PickupPointPickerSheet widgets for modular pickup-point rendering and selection. ShippingListItem groupValue made nullable; radio widget replaced with custom Icon showing checked/unchecked state. CheckoutPage registers _handleOrderStatus listener and uses hasValidDeliverySelection for payment button enablement.
Test Suite Updates
test/checkout_view_model_test.dart
Replaced Mockito mocks with FakeCheckoutRepository and FakeNavigationProvider; added assertions for pickupPoints emptiness on init; adjusted checkout-readiness test to require delivery choice and address confirmation; added tests for pickup-point parsing failures, successful population from documented response shape, fetching from address with default radius, and clearing selectedPickupPoint on delivery mode switch.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DeliveryOptions
  participant ViewModel as CheckoutViewModel
  participant Repo as CheckoutRepository
  participant Sheet as PickupPointPickerSheet
  participant Backend

  User->>DeliveryOptions: Select pickup delivery
  DeliveryOptions->>DeliveryOptions: _selectPickupDelivery()
  DeliveryOptions->>ViewModel: setDeliveryChoice('pickup_point')
  DeliveryOptions->>ViewModel: fetchPickupPointsForShippingAddress()
  ViewModel->>Repo: fetchPickupPoints(country, address, radius)
  Repo->>Backend: GET /shipping/pickup-points?country=...&address=...&radius=5000
  Backend-->>Repo: List of PickupPoint JSON objects
  Repo-->>ViewModel: Result.success(response)
  ViewModel->>ViewModel: _parsePickupPointList(response)
  ViewModel-->>DeliveryOptions: Status updated, pickupPoints populated
  DeliveryOptions->>Sheet: showModalBottomSheet(pickupPoints)
  User->>Sheet: Select a pickup point
  Sheet-->>DeliveryOptions: Selected PickupPoint
  DeliveryOptions->>ViewModel: setSelectedPickupPoint(point)
  DeliveryOptions->>User: Display selected point details
Loading
sequenceDiagram
  participant User
  participant CheckoutUI as Checkout Page
  participant ViewModel as CheckoutViewModel
  participant Repo as CheckoutRepository
  participant Firestore
  participant OrderBackend as Order Backend

  User->>CheckoutUI: Tap Pay button
  CheckoutUI->>ViewModel: processCheckout()
  ViewModel->>ViewModel: createOrder() with deliveryMethod + pickupPoint
  ViewModel->>Firestore: storeOrderInFirestore(order with delivery info)
  Firestore-->>ViewModel: Document stored
  ViewModel->>OrderBackend: POST /orders with deliveryMethod and pickupPoint
  OrderBackend-->>ViewModel: Order confirmation
  ViewModel->>CheckoutUI: notifyListeners() → _handleOrderStatus
  CheckoutUI->>User: Order success / navigation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Poem

🐰 The locker gives way to the point divine,
Where pickups now dance in a modular line.
With address and radius, we fetch from afar,
And select where to meet with a bottom-sheet's char.
From model to order, delivery's done right! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and clearly summarizes the main change: adding pickup point selection functionality to the checkout flow for the pickup_point delivery method.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cherrypickuppointselection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CherryCIC CherryCIC marked this pull request as ready for review May 20, 2026 10:53
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02506995dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

int radius = AddressConstants.pickupPointSearchRadiusMeters,
}) async {
_status = Status.loading;
selectedPickupPoint = null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve selected pickup point until refresh succeeds

Clearing selectedPickupPoint at the start of fetchPickupPoints causes a regression where an already-valid pickup selection is lost before new data is available. If the refresh request fails or returns an empty payload (for example, transient network/API issues after the user taps pickup again), checkout becomes blocked even though the previous pickup point was still usable. Keep the existing selection until the new list is successfully loaded and only clear it if it is no longer present in the refreshed results.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/checkout/checkout_view_model.dart (1)

507-514: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unify delivery method key in order payloads.

createOrder() sends deliveryMethod while other checkout payload builders use delivery_method. This contract drift can cause delivery mode to be dropped/misread downstream.

Proposed fix
-      "deliveryMethod": deliveryChoice,
+      "delivery_method": deliveryChoice,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/checkout_view_model.dart` around lines 507 - 514, The
order payload in createOrder() uses the key "deliveryMethod" but other checkout
payload builders expect "delivery_method", causing a contract mismatch; update
the Map<String, dynamic> orderData construction (the orderData variable in
createOrder()) to use "delivery_method": deliveryChoice instead of
"deliveryMethod" so the payload aligns with other builders and downstream
consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/checkout/checkout_repository.dart`:
- Line 50: The fallback error message inside fetchNearestInPosts uses a generic
"Pickup points..." text which is misleading for locker flows; update the
fallback used with result.error (the expression result.error ?? '...') to a
locker-specific message such as "Lockers currently unavailable, please try again
later" so UX and diagnostics correctly reflect the locker path.
- Around line 11-15: Update the fetchPickupPoints interface declaration so its
radius parameter matches the implementation: make radius a named parameter with
a default value (int radius = AddressConstants.pickupPointSearchRadiusMeters)
instead of an optional nullable int; change the signature of Future<Result>
fetchPickupPoints({ required String country, required String address, int
radius, }); to use the defaulted radius to align with the concrete
implementation.

In `@lib/features/checkout/models/pickup_point.dart`:
- Line 74: The pickup-point payload currently hardcodes 'inpost_gb' when carrier
is missing; update the serialization in the PickupPoint model (the carrier
field/ toJson/map logic) to not inject a legacy provider: replace the ternary
that returns 'inpost_gb' with a nullable/omitted value (e.g. set carrier to null
or skip the 'carrier' key when carrier is null/empty), e.g. change the
expression for 'carrier' in PickupPoint.toJson (or the map builder) from
returning 'inpost_gb' to returning null or not emitting the key, and ensure
callers that consume PickupPoint handle a nullable/missing carrier.

In `@lib/features/checkout/widgets/delivery_options.dart`:
- Line 303: Replace the hardcoded UI strings used in the delivery options widget
with localized entries from AppStrings: change the
Text('${model.pickupPoints.length} pick-up points nearby') and the 'Open this
week' label in the same widget to use AppStrings (e.g.,
AppStrings.pickUpPointsNearby and AppStrings.openThisWeek) and interpolate the
pickupPoints length into the localized template; if those keys do not exist yet,
add parameterized entries to AppStrings (with a placeholder for the count) and
update any usages to call the localization accessor (e.g.,
AppStrings.pickUpPointsNearby(count)) so all user-visible text in the widget
uses the l10n layer.
- Around line 286-293: The tap and button handlers call the async callback
onChoose() without awaiting it (in the anonymous closures used for onPressed and
onTap), so convert those closures to async and await onChoose() to ensure errors
are captured and ordering is preserved; locate the anonymous handlers
referencing onChoose (the onPressed: () { onChoose(); } and onTap: () {
onChoose(); } closures in delivery_options.dart) and change them to async
closures that await onChoose() (e.g., onPressed: () async { await onChoose(); }
and onTap: () async { await onChoose(); }).

In `@lib/features/checkout/widgets/shipping_list_item.dart`:
- Around line 32-35: The trailing Icon used to reflect selection state
(isSelected) lacks accessibility semantics; update the ListTile trailing so
screen readers understand this is a mutually-exclusive radio selection — either
replace the Icon with Flutter's Radio widget bound to isSelected and the same
onTap handler (keeping ListTile and onTap behavior), or wrap the existing Icon
with a Semantics widget and set checked: isSelected and
inMutuallyExclusiveGroup: true (also provide a label if appropriate) so
assistive tech correctly announces selection state and group behavior.

---

Outside diff comments:
In `@lib/features/checkout/checkout_view_model.dart`:
- Around line 507-514: The order payload in createOrder() uses the key
"deliveryMethod" but other checkout payload builders expect "delivery_method",
causing a contract mismatch; update the Map<String, dynamic> orderData
construction (the orderData variable in createOrder()) to use "delivery_method":
deliveryChoice instead of "deliveryMethod" so the payload aligns with other
builders and downstream consumers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6415df9-5026-46bc-95f0-d3a22f5118f2

📥 Commits

Reviewing files that changed from the base of the PR and between 132c18f and 0250699.

📒 Files selected for processing (11)
  • lib/core/config/app_strings.dart
  • lib/core/services/network/api_endpoints.dart
  • lib/features/checkout/checkout_page.dart
  • lib/features/checkout/checkout_repository.dart
  • lib/features/checkout/checkout_view_model.dart
  • lib/features/checkout/constants/address_constants.dart
  • lib/features/checkout/models/pickup_point.dart
  • lib/features/checkout/widgets/delivery_options.dart
  • lib/features/checkout/widgets/share_location_dialog.dart
  • lib/features/checkout/widgets/shipping_list_item.dart
  • test/checkout_view_model_test.dart
💤 Files with no reviewable changes (1)
  • lib/features/checkout/widgets/share_location_dialog.dart

Comment on lines +11 to +15
Future<Result> fetchPickupPoints({
required String country,
required String address,
int radius,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Dart null safety, is an optional named parameter like {int radius}valid withoutrequired or a default value?

💡 Result:

In Dart with null safety, an optional named parameter like {int radius} is not valid because its type is non-nullable [1][2]. When a parameter is optional and no default value is provided, Dart implicitly assigns it a default value of null [1][2]. If the parameter's type is non-nullable (such as int), this causes a compile-time error because the implicit null value conflicts with the non-nullable type [1][2]. To make such a parameter valid, you must choose one of the following approaches [3][1]: 1. Make the type nullable: Use {int? radius}. This explicitly allows null to be the default value [3][1]. 2. Provide a default value: Use {int radius = 0} (or another appropriate default). This ensures the parameter will have a non-null value if omitted [3][1][2]. 3. Mark it as required: Use {required int radius}. This removes the optionality, forcing the caller to provide the argument [3][1].

Citations:


🏁 Script executed:

# Check the actual file content around lines 11-15
cat -n lib/features/checkout/checkout_repository.dart | head -20

Repository: Cherry-CIC/MVP

Length of output: 1021


🏁 Script executed:

# Find the implementation of fetchPickupPoints to see what default is used
rg -A 10 "fetchPickupPoints" lib/features/checkout/ -g "*.dart"

Repository: Cherry-CIC/MVP

Length of output: 8358


🏁 Script executed:

# Find AddressConstants to verify the constant exists
fd -t f "AddressConstants" lib/
rg "pickupPointSearchRadiusMeters" --type dart

Repository: Cherry-CIC/MVP

Length of output: 506


Fix the radius parameter declaration in the interface to match the implementation.

The interface declares int radius as an optional named parameter without required or a default value, which violates Dart null-safety. The concrete implementation correctly provides int radius = AddressConstants.pickupPointSearchRadiusMeters as default. Align the interface signature with the implementation to resolve the compile-time error.

Proposed fix
  Future<Result> fetchPickupPoints({
    required String country,
    required String address,
-   int radius,
+   int radius = AddressConstants.pickupPointSearchRadiusMeters,
  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Future<Result> fetchPickupPoints({
required String country,
required String address,
int radius,
});
Future<Result> fetchPickupPoints({
required String country,
required String address,
int radius = AddressConstants.pickupPointSearchRadiusMeters,
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/checkout_repository.dart` around lines 11 - 15, Update
the fetchPickupPoints interface declaration so its radius parameter matches the
implementation: make radius a named parameter with a default value (int radius =
AddressConstants.pickupPointSearchRadiusMeters) instead of an optional nullable
int; change the signature of Future<Result> fetchPickupPoints({ required String
country, required String address, int radius, }); to use the defaulted radius to
align with the concrete implementation.

return Result.failure(
result.error ??
'Pickup points currently unavailable, please try again later',
result.error ?? 'Pickup points currently unavailable, please try again later',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a locker-specific fallback message in fetchNearestInPosts.

The current fallback says “Pickup points…” inside the locker fetch path, which is misleading for UX and diagnostics.

Proposed fix
-          result.error ?? 'Pickup points currently unavailable, please try again later',
+          result.error ?? 'Pickup lockers currently unavailable, please try again later',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result.error ?? 'Pickup points currently unavailable, please try again later',
result.error ?? 'Pickup lockers currently unavailable, please try again later',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/checkout_repository.dart` at line 50, The fallback
error message inside fetchNearestInPosts uses a generic "Pickup points..." text
which is misleading for locker flows; update the fallback used with result.error
(the expression result.error ?? '...') to a locker-specific message such as
"Lockers currently unavailable, please try again later" so UX and diagnostics
correctly reflect the locker path.

'city': city,
'postalCode': postalCode,
'country': country,
'carrier': carrier == null || carrier!.isEmpty ? 'inpost_gb' : carrier,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid hard-coding a legacy carrier in generic pickup-point payloads.

Defaulting carrier to inpost_gb injects provider-specific data even when no carrier was returned/selected, which can produce incorrect order payloads.

Proposed fix
-      'carrier': carrier == null || carrier!.isEmpty ? 'inpost_gb' : carrier,
+      if (carrier != null && carrier!.isNotEmpty) 'carrier': carrier,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'carrier': carrier == null || carrier!.isEmpty ? 'inpost_gb' : carrier,
if (carrier != null && carrier!.isNotEmpty) 'carrier': carrier,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/models/pickup_point.dart` at line 74, The pickup-point
payload currently hardcodes 'inpost_gb' when carrier is missing; update the
serialization in the PickupPoint model (the carrier field/ toJson/map logic) to
not inject a legacy provider: replace the ternary that returns 'inpost_gb' with
a nullable/omitted value (e.g. set carrier to null or skip the 'carrier' key
when carrier is null/empty), e.g. change the expression for 'carrier' in
PickupPoint.toJson (or the map builder) from returning 'inpost_gb' to returning
null or not emitting the key, and ensure callers that consume PickupPoint handle
a nullable/missing carrier.

Comment on lines +286 to +293
onPressed: () {
onChoose();
},
child: const Text(AppStrings.checkoutChangePickupPoint),
),
onTap: () {
onChoose();
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Await async pickup actions in tap handlers.

At Line 286 and Line 305, onChoose() is invoked without awaiting its Future<void>, so async errors can escape the handler and be harder to control. Make these handlers async and await onChoose().

Suggested fix
-          trailing: TextButton(
-            onPressed: () {
-              onChoose();
-            },
+          trailing: TextButton(
+            onPressed: () async {
+              await onChoose();
+            },
             child: const Text(AppStrings.checkoutChangePickupPoint),
           ),
-          onTap: () {
-            onChoose();
-          },
+          onTap: () async {
+            await onChoose();
+          },
...
-        onTap: () {
-          onChoose();
-        },
+        onTap: () async {
+          await onChoose();
+        },

Also applies to: 305-307

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/widgets/delivery_options.dart` around lines 286 - 293,
The tap and button handlers call the async callback onChoose() without awaiting
it (in the anonymous closures used for onPressed and onTap), so convert those
closures to async and await onChoose() to ensure errors are captured and
ordering is preserved; locate the anonymous handlers referencing onChoose (the
onPressed: () { onChoose(); } and onTap: () { onChoose(); } closures in
delivery_options.dart) and change them to async closures that await onChoose()
(e.g., onPressed: () async { await onChoose(); } and onTap: () async { await
onChoose(); }).

contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
leading: const Icon(Icons.map_outlined),
title: const Text(AppStrings.checkoutChoosePickupPoint),
subtitle: model.pickupPoints.isEmpty ? null : Text('${model.pickupPoints.length} pick-up points nearby'),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize newly added user-visible strings.

At Line 303 and Line 449, UI text is hardcoded ("pick-up points nearby", "Open this week"). These should come from AppStrings to avoid l10n regressions.

Also applies to: 449-449

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/widgets/delivery_options.dart` at line 303, Replace the
hardcoded UI strings used in the delivery options widget with localized entries
from AppStrings: change the Text('${model.pickupPoints.length} pick-up points
nearby') and the 'Open this week' label in the same widget to use AppStrings
(e.g., AppStrings.pickUpPointsNearby and AppStrings.openThisWeek) and
interpolate the pickupPoints length into the localized template; if those keys
do not exist yet, add parameterized entries to AppStrings (with a placeholder
for the count) and update any usages to call the localization accessor (e.g.,
AppStrings.pickUpPointsNearby(count)) so all user-visible text in the widget
uses the l10n layer.

Comment on lines +32 to 35
trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
color: isSelected ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurfaceVariant,
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, find the file and check its contents
fd "shipping_list_item.dart"

Repository: Cherry-CIC/MVP

Length of output: 112


🏁 Script executed:

# Read the file to see the code context
cat -n lib/features/checkout/widgets/shipping_list_item.dart

Repository: Cherry-CIC/MVP

Length of output: 1545


🏁 Script executed:

# Check git history for this file to see if it was previously using Radio
git log --oneline -n 20 lib/features/checkout/widgets/shipping_list_item.dart

Repository: Cherry-CIC/MVP

Length of output: 105


🏁 Script executed:

# Check the diff of the commit to see what changed
git show 0250699:lib/features/checkout/widgets/shipping_list_item.dart

Repository: Cherry-CIC/MVP

Length of output: 1258


🏁 Script executed:

# Let me check if there are other similar radio selection patterns in the codebase
rg -t dart "Radio\s*\(" --max-count 20

Repository: Cherry-CIC/MVP

Length of output: 40


🏁 Script executed:

# Check if there's a commit before this one that might show the previous state
git log --oneline --all | head -20

Repository: Cherry-CIC/MVP

Length of output: 1381


🏁 Script executed:

# Check if this is a new file or if it was changed in the commit
git show --name-status 0250699

Repository: Cherry-CIC/MVP

Length of output: 18066


🏁 Script executed:

# Check Flutter's ListTile semantics in Material Design context
# Search for any Semantics wrappers or similar patterns in the codebase
rg -t dart "Semantics\s*\(" lib/features/checkout/ -A 3

Repository: Cherry-CIC/MVP

Length of output: 40


Add semantic context for radio-button selection pattern.

This widget uses Icon to display radio button state but lacks semantic markup for screen readers. The ListTile's onTap provides interaction, but assistive tech won't understand this represents a mutually-exclusive group selection. Either use the Radio widget instead, or wrap the Icon in a Semantics widget with checked: isSelected and inMutuallyExclusiveGroup: true so screen readers correctly announce the selection state and group behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/checkout/widgets/shipping_list_item.dart` around lines 32 - 35,
The trailing Icon used to reflect selection state (isSelected) lacks
accessibility semantics; update the ListTile trailing so screen readers
understand this is a mutually-exclusive radio selection — either replace the
Icon with Flutter's Radio widget bound to isSelected and the same onTap handler
(keeping ListTile and onTap behavior), or wrap the existing Icon with a
Semantics widget and set checked: isSelected and inMutuallyExclusiveGroup: true
(also provide a label if appropriate) so assistive tech correctly announces
selection state and group behavior.

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.

1 participant