Add scrim when ExpandedFab is open #28

Closed
opened 2026-06-28 13:43:00 +00:00 by admin · 0 comments
Owner

Enhancement / UX

The ExpandedFab on the Expenses screen (app/src/main/java/dev/achmad/ledgerr/ui/screens/expenses/ExpenseListScreen.kt:126-159) reveals its mini-FABs without any backdrop. The list behind the FAB stays fully visible and interactive, so the user has no visual cue that the FAB is the active surface, and tapping the list does not dismiss the expansion.

Current codeapp/src/main/java/dev/achmad/ledgerr/ui/components/ExpandedFab.kt:28-58

ExpandedFab renders only the column of mini-FABs and the main FAB. There is no scrim.

Expected behavior

When ExpandedFab.expanded == true, a semi-transparent scrim covers the underlying content. The scrim:

  • Fades in/out in sync with the mini-FABs (use the same fadeIn / fadeOut animation).
  • Is full-screen (or at least covers the area behind the FAB and list — a fillMaxSize Box overlay is fine).
  • Uses a Material 3 scrim color (e.g. MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)) so it adapts to light/dark.
  • Is clickable; tapping it calls onToggle (i.e. dismisses the FAB) and does not propagate to the list below.
  • Does not block the system back gesture (system back should also dismiss the FAB — see notes).

Acceptance criteria

  • Tapping the main FAB shows the scrim and the mini-FABs together; tapping the main FAB again, tapping the scrim, or pressing the system back button dismisses both.
  • The scrim never appears when expanded == false.
  • The animation matches the mini-FAB expand/collapse (no jank, no scrim popping in after the FABs are already visible).
  • The scrim does not intercept clicks that should reach the top app bar (e.g. tab switching) — placing it inside the Scaffold body, below the Column containing the tabs/Pager is fine; placing it above the whole Scaffold is not acceptable.

Implementation notes (not prescriptive)

  • The scrim can live inside ExpandedFab (e.g. as an AnimatedVisibility { Box(Modifier.fillMaxSize().clickable(onClick = onToggle).background(...)) } rendered as a sibling of the existing column) and require the screen to render ExpandedFab in a Box so it can stack above the list. The current floatingActionButton slot in the Scaffold does not stack a scrim above the body, so the call site (ExpenseListScreen) will need to be refactored to use a Box containing both the list content and the FAB with its scrim — or the scrim is rendered inside the Column body and the body is wrapped in a Box that also contains the FAB. Coordinate the chosen approach with how Scaffold lays out the FAB slot (FAB is already a floating overlay).
  • Back-button dismissal is a "nice to have" — use BackHandler(enabled = expanded) { onToggle() } inside the screen if going for it. Skip it for the first pass if it complicates the layout.
**Enhancement / UX** The `ExpandedFab` on the Expenses screen (`app/src/main/java/dev/achmad/ledgerr/ui/screens/expenses/ExpenseListScreen.kt:126-159`) reveals its mini-FABs without any backdrop. The list behind the FAB stays fully visible and interactive, so the user has no visual cue that the FAB is the active surface, and tapping the list does not dismiss the expansion. **Current code** — `app/src/main/java/dev/achmad/ledgerr/ui/components/ExpandedFab.kt:28-58` `ExpandedFab` renders only the column of mini-FABs and the main FAB. There is no scrim. **Expected behavior** When `ExpandedFab.expanded == true`, a semi-transparent scrim covers the underlying content. The scrim: - Fades in/out in sync with the mini-FABs (use the same `fadeIn` / `fadeOut` animation). - Is full-screen (or at least covers the area behind the FAB and list — a `fillMaxSize` Box overlay is fine). - Uses a Material 3 scrim color (e.g. `MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)`) so it adapts to light/dark. - Is clickable; tapping it calls `onToggle` (i.e. dismisses the FAB) and does **not** propagate to the list below. - Does not block the system back gesture (system back should also dismiss the FAB — see notes). **Acceptance criteria** - Tapping the main FAB shows the scrim and the mini-FABs together; tapping the main FAB again, tapping the scrim, or pressing the system back button dismisses both. - The scrim never appears when `expanded == false`. - The animation matches the mini-FAB expand/collapse (no jank, no scrim popping in after the FABs are already visible). - The scrim does not intercept clicks that should reach the top app bar (e.g. tab switching) — placing it inside the `Scaffold` body, *below* the `Column` containing the tabs/`Pager` is fine; placing it above the whole `Scaffold` is **not** acceptable. **Implementation notes (not prescriptive)** - The scrim can live inside `ExpandedFab` (e.g. as an `AnimatedVisibility { Box(Modifier.fillMaxSize().clickable(onClick = onToggle).background(...)) }` rendered as a sibling of the existing column) and require the screen to render `ExpandedFab` in a `Box` so it can stack above the list. The current `floatingActionButton` slot in the `Scaffold` does not stack a scrim above the body, so the call site (`ExpenseListScreen`) will need to be refactored to use a `Box` containing both the list content and the FAB with its scrim — or the scrim is rendered inside the `Column` body and the body is wrapped in a `Box` that also contains the FAB. Coordinate the chosen approach with how `Scaffold` lays out the FAB slot (FAB is already a floating overlay). - Back-button dismissal is a "nice to have" — use `BackHandler(enabled = expanded) { onToggle() }` inside the screen if going for it. Skip it for the first pass if it complicates the layout.
admin closed this issue 2026-06-28 14:34:31 +00:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/ledgerr#28