Skip to content

Reusable mobile bottom-sheet template + swap modal migration#246

Open
nikfromkeplr wants to merge 1 commit into
mobile-ui-vibe-coding-polishing-2from
mobile-asset-selector-bottom-sheet
Open

Reusable mobile bottom-sheet template + swap modal migration#246
nikfromkeplr wants to merge 1 commit into
mobile-ui-vibe-coding-polishing-2from
mobile-asset-selector-bottom-sheet

Conversation

@nikfromkeplr

Copy link
Copy Markdown
Collaborator

Summary

Introduces a reusable mobile bottom-sheet template and migrates the swap modals onto it, replacing the fragmented per-modal presentations with one standard iOS/Android sheet.

The template lives in lib/src/core/layout/mobile/mobile_sheet.dart:

  • showMobileSheet — root-navigator, edge-to-edge showModalBottomSheet wrapper (covers the floating tab bar, transparent surface, neutral scrim).
  • MobileSheetScaffold — top-rounded surface with a grabber, a centered title flanked by absolute back (2nd-level views) and close buttons, keyboard avoidance, drag-to-dismiss, and a rubber-band over-pull spring.
  • MobileSheetFormBody — adaptive form body: content above full-width action buttons pinned to the bottom.

Content-driven height (3 levels)

A formBody sheet sizes itself to its content, clamped between a 400px floor and the top-gap cap:

Level Content Height Body
1 short min-height floor centered
2 medium hugs the content fits
3 tall caps below the top gap scrolls

In every level the action buttons stay pinned to the bottom. fillBody keeps a fixed-min Expanded slot for list bodies (contacts); expand fills the screen for long scroll lists (asset selector).

Migrated modals

  • Swap asset selector (full-screen list + skeleton loading state)
  • Slippage editor (level 1, centered)
  • Recipient / refund address editor (level 2, hugs; morphs to contacts with a back chevron)
  • Address-book contact picker (fills, centered empty state)

Scope / safety

  • All sheet styling is mobile-only via kAppFormFactor; desktop layouts are unchanged (the shared SwapModalButtons full-width behavior is form-factor gated, desktop keeps its original pill layout).
  • DRY: over-pull/slide math and the cap-height formula are shared helpers; the back/close buttons reuse a single corner-button widget; spacing/radii/typography use existing tokens.

Tests

  • New test/core/layout/mobile/mobile_sheet_form_body_test.dart pins all three height levels (floor / hug / cap + scroll, actions pinned).
  • New mobile-tagged sheet tests for the asset selector and slippage; updated swap modal route test (morph + back navigation).
  • Verified: mobile sheet suites 19/19, desktop swap + send + address-book 227 passing, flutter analyze clean.

🤖 Generated with Claude Code

Introduce showMobileSheet + MobileSheetScaffold + MobileSheetFormBody as
the standard mobile modal: a top-rounded, edge-to-edge sheet with a
grabber, a centered title flanked by absolute back/close buttons,
drag-to-dismiss plus a rubber-band over-pull spring, and an adaptive,
content-driven body.

The formBody template sizes itself across three levels — rest at the
min-height floor with the body centered, grow to hug the content, or cap
below the top gap and scroll — always with full-width action buttons
pinned to the bottom edge. fillBody keeps a fixed-min Expanded slot for
list bodies (contacts), and expand fills the screen for long scroll lists
(asset selector).

Migrate the swap asset selector, slippage, recipient/refund address, and
address-book contact-picker modals onto the template. All sheet styling is
mobile-only (kAppFormFactor); desktop layouts are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nikfromkeplr nikfromkeplr requested review from Thunnini and piatoss3612 and removed request for piatoss3612 June 16, 2026 11:33
@Thunnini Thunnini changed the base branch from main to mobile-ui-vibe-coding-polishing-2 June 16, 2026 11:34
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

dismissPresentedSheet()
pendingResult = result

P2 Badge Resolve the active date picker before replacing it

When pickDate is invoked while a previous calendar sheet is still being dismissed (for example a rapid double tap on the date field or an e2e cancel/reopen), dismissPresentedSheet() relies on the old sheet's viewDidDisappear to call finish(nil), but this line immediately overwrites pendingResult with the new call's result. The old dismissal then completes the new Dart future with null, leaves the original future unresolved, and the newly presented sheet can no longer return the selected date because pendingResult has been cleared.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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.

2 participants