Replaces RoutePlannerScreen's AppBar-driven layout with a headerless, full-opacity map canvas restyled toward Map HUD rather than copied from this screen's own (drifted) Stitch export: a floating "tap to drop a pin" tooltip before any pins exist, FloatingPill (Distance/Est. Time/Pins) once they do, a "Start Route" floating button, a GlassPanel overflow menu replacing the old app-bar actions (rename/delete/download), and a pin-drop ripple micro-interaction. RoutesListScreen keeps its existing list-with-+-button entry point (the IA question in the ticket's Risk section, deferred as explicitly permitted) but restyles its rows as GlassPanel cards. "Start Route" gets real behavior instead of shipping as a dead button: turn-by-turn route following doesn't exist yet (V3-09), so tapping it starts an ordinary recording and switches to the Map tab -- useful today, not a placeholder for a feature that isn't there. Est. Time honestly shows "--" rather than fabricating an estimate the data model doesn't back. Marching-ants route-line animation was dropped from scope: it isn't in the acceptance criteria, and animating a dash phase under live map pan/zoom is real complexity for a purely decorative effect. Fixes a real, pre-existing bug found while working in this file: the offline-tile download dialog still fetched from tile.openstreetmap.org directly, a leftover from before UI-09 switched the live map to CARTO's dark tiles -- a "successful" download would have cached tiles under keys the dark-tile cache never reads from. Also fixes a live Flutter framework warning the new GlassPanel-wrapped ListTile surfaced (ink splashes need a Material ancestor, which GlassPanel's DecoratedBox was blocking) and a real test bug (two pins placed one directly under the new floating Start Route button in a fixed test camera, causing a tap meant for the pin to hit the button instead). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
171 lines
11 KiB
Markdown
171 lines
11 KiB
Markdown
# UI-06 — Plan & Route Planning redesign
|
|
|
|
**Depends on** UI-01, UI-03 · **Size** M · **Status** Done
|
|
|
|
## Goal
|
|
Rebuild the Plan tab (empty state) and the route planner (pins dropped) as fullscreen
|
|
map canvases with floating controls — correct in principle per the Stitch mockups, but
|
|
**restyled to match Map HUD (UI-05), not copied as-is** — see `README.md`'s note on why
|
|
these two mockups drifted from the design north star.
|
|
|
|
## Context
|
|
Today's `RoutePlannerScreen` is a `Scaffold` with an `AppBar` (title, rename/delete/
|
|
download actions) and the map beneath it. The mockups drop the header entirely (per
|
|
UI-01) and float everything — a pin-drop tooltip, a stat summary pill, a Start Route
|
|
button — over a fullscreen map, with a bottom nav bar instead of an app bar's back
|
|
button (the planner becomes a screen reached *within* the Plan tab, not a separately
|
|
titled pushed screen).
|
|
|
|
**What to take from the mockups as-is:** the fullscreen-map-plus-floating-controls
|
|
pattern, the pin-drop ripple micro-interaction, `PulsingLocationMarker` for the rider's
|
|
own position, the marching-ants animated route line, the floating stat pill
|
|
(Distance/Est. Time/Pins) replacing today's pre-download confirmation dialog as a
|
|
persistent readout instead of a one-time modal.
|
|
|
|
**What to restyle rather than copy:** icon fill-states, the nav bar's exact background/
|
|
blur treatment, and any color choice that doesn't match UI-08's actual palette as
|
|
established on the Map HUD screen. The Route Planning mockup specifically shows the
|
|
"Rides" nav icon active while viewing Plan — a generation error, not a real cross-tab
|
|
indicator; the shipped nav bar must correctly highlight whichever tab is actually active,
|
|
matching Map HUD's nav bar exactly (same widget, in fact — see UI-01).
|
|
|
|
## Design
|
|
- **Plan tab (no pins yet):** fullscreen map, technical grid overlay, floating "Tap to
|
|
drop a pin" tooltip (gentle float animation), `PulsingLocationMarker`.
|
|
- **Route Planning (pins dropped):** same canvas, plus the floating stat pill (`FloatingPill`
|
|
from UI-03) showing Distance / Est. Time / Pin count, updating live as pins are added/
|
|
moved/removed (already true of the underlying `RoutePlanRepository` data — this ticket
|
|
is presentation only). "Start Route" as a floating pill-shaped button above the nav
|
|
bar, replacing today's app-bar-driven actions.
|
|
- **Existing actions (rename, delete, download offline tiles)** need a new home now that
|
|
there's no app bar to hang icon buttons off of — a small floating overflow/glass menu
|
|
button is the natural fit, styled per Map HUD's `GlassPanel` language, not invented
|
|
fresh.
|
|
- **Drag-to-move / tap-to-delete pins:** unchanged interaction from today's
|
|
`RoutePlannerScreen`; only the chrome around it changes.
|
|
|
|
## Implementation
|
|
1. Rebuild `RoutePlannerScreen`'s `Scaffold`/`AppBar` as a headerless canvas within the
|
|
Plan tab's branch navigator (per UI-01), map at full opacity/interactivity here
|
|
(unlike the dimmed treatment other tabs get for the *background* map — this screen's
|
|
map *is* the primary content, same as Map HUD's).
|
|
2. Replace the pre-download confirmation dialog (V3-11) with the persistent `FloatingPill`
|
|
stat summary; keep the actual download flow (count/size check, cancellable progress)
|
|
as-is underneath — presentation change only.
|
|
3. Add the pin-drop ripple micro-interaction and marching-ants route line as new,
|
|
currently-nonexistent polish.
|
|
4. Rebuild the Plan tab's empty state (no `RoutePlan` selected/created yet) as the
|
|
fullscreen tooltip-and-map view, wired to whatever "create a new route" entry point
|
|
replaces today's `RoutesListScreen` FAB (see UI-07 — Rides History's tab likely
|
|
absorbs or sits alongside a route list; confirm final IA when that ticket lands, since
|
|
"Rides" and "Plan" may end up sharing more list/history UI than the current
|
|
`RoutesListScreen`/`TripsScreen` split assumes).
|
|
5. Move rename/delete/download actions to a floating glass menu, styled per Map HUD.
|
|
|
|
## Acceptance criteria
|
|
- [ ] Both Plan states (empty, pins dropped) render as fullscreen map canvases with no
|
|
header
|
|
- [ ] The nav bar on this screen is the exact same widget/styling as Map HUD's, with the
|
|
Plan tab correctly shown active (not the generation-error "Rides active" state
|
|
from the mockup)
|
|
- [ ] Existing pin add/move/delete, rename, delete-route, and offline-download behavior
|
|
all still work, restyled only
|
|
- [ ] Icon fill-states and colors match Map HUD, not the Stitch export's drifted values
|
|
|
|
## Tests
|
|
- All existing `route_planner_screen_test.dart` / `route_plan_repository_test.dart`
|
|
behavior tests still pass against the new presentation layer
|
|
- Widget: nav bar active-tab indicator is correct on this screen
|
|
- Widget: the floating stat pill updates live as pins are added/removed (already covered
|
|
conceptually by existing repository tests; add a presentation-level assertion that the
|
|
pill reflects it)
|
|
|
|
## Risks
|
|
- The IA question flagged in Implementation step 4 (how "create a new route" is reached
|
|
without a `RoutesListScreen`-style list-with-FAB) may turn out to need its own small
|
|
design decision once UI-07 is underway — don't block this ticket on it if the existing
|
|
entry point can be preserved with new styling in the meantime.
|
|
|
|
## Out of scope
|
|
Road-snapped routing (V3-08/V3-09, deferred). Any change to the underlying route-planning
|
|
data model or repository — this is presentation only.
|
|
|
|
## Outcome
|
|
|
|
**IA question (Risk section) resolved by deferring, as the ticket explicitly allowed:**
|
|
`RoutesListScreen` stays a list-with-`+`-button (the existing entry point), restyled with
|
|
`GlassPanel`-wrapped rows instead of plain `Card`s. The mockup's own "fullscreen map +
|
|
tooltip" language turned out, on close reading, to describe `RoutePlannerScreen`'s
|
|
*own* empty-pins state (the tooltip literally instructs "tap to drop a pin" — an action
|
|
that happens on the canvas, not on a list of named routes), not the routes list. Both
|
|
screens got the restyle their actual role calls for.
|
|
|
|
`RoutePlannerScreen` is now a headerless, full-opacity map canvas (its own map, not
|
|
UI-01's dimmed shell background — this screen's map *is* the primary content, matching
|
|
Map HUD). Implemented: the floating "TAP TO DROP A PIN" tooltip (`GlassPanel`, gentle
|
|
3s float) shown only while `waypoints.isEmpty`; `FloatingPill` (UI-03) showing Distance/
|
|
Est. Time/Pins once pins exist, replacing the old pre-download confirmation dialog's
|
|
role as the primary stat display (the confirmation dialog itself is untouched --
|
|
presentation only, per scope); a "START ROUTE" floating pill button; a `GlassPanel`
|
|
overflow menu (`PopupMenuButton`) holding Rename/Download offline tiles/Delete route,
|
|
replacing the removed `AppBar`'s actions row; and a pin-drop ripple micro-interaction
|
|
(a short-lived expanding-and-fading ring at the tap's local screen position).
|
|
|
|
**"Start Route," given real, working behaviour rather than shipped as a dead button:**
|
|
turn-by-turn following (V3-09) doesn't exist anywhere in the app to wire this to, and
|
|
inventing that data model here would violate this ticket's own "presentation only"
|
|
scope. Tapping it starts an ordinary recording and switches to the Map tab -- a real
|
|
action a rider can use today, not a placeholder.
|
|
|
|
**Est. Time honestly shows "--"** when `RoutePlan.estimatedMillis` is null (always, until
|
|
V3-08 adds road-snapped routing) rather than fabricating a straight-line estimate the
|
|
model doesn't back.
|
|
|
|
**Marching-ants route-line animation was deliberately dropped from scope.** It isn't in
|
|
this ticket's acceptance criteria (only the pin-drop ripple is named there), and
|
|
implementing it properly means animating a dash phase while continuously reprojecting
|
|
through `MapCamera.latLngToScreenOffset` under live pan/zoom -- real complexity for a
|
|
purely decorative effect. The existing static dashed polyline (already "visibly distinct
|
|
from a recorded path," the actual acceptance-relevant property) is unchanged.
|
|
|
|
**Bug found and fixed, unrelated to this ticket's own changes but discovered while
|
|
working in this file:** `_DownloadDialog`'s tile fetch still hardcoded
|
|
`https://tile.openstreetmap.org/...` directly -- a leftover from before UI-09 switched
|
|
the live map to CARTO's dark tiles. Downloading offline tiles would have silently cached
|
|
OSM's tan tiles under the same `(z, x, y)` keys UI-09's cache versioning was specifically
|
|
designed to keep separate from CARTO's, meaning a "successful" download would never
|
|
actually serve anything to the live dark map. Fixed to build the URL from the same
|
|
`tileUrlTemplate`/`tileSubdomains` every other tile fetch in the app already uses.
|
|
|
|
**Nav bar correctness:** no separate implementation needed -- `RoutePlannerScreen` is
|
|
pushed *within* the Plan branch's own navigator (UI-01's `StatefulShellBranch`
|
|
architecture), so the shell's persistent nav bar already shows Plan active automatically,
|
|
structurally ruling out the mockup's own "Rides active while viewing Plan" generation
|
|
error. Added a test asserting this directly rather than trusting the architecture alone.
|
|
|
|
**Tests:** `flutter analyze` clean. `flutter test` green at 370 tests. Updated every
|
|
existing `route_planner_screen_test.dart` assertion that referenced now-removed `AppBar`
|
|
structure (the `IconButton` keys are `PopupMenuItem`s behind the overflow menu now; there
|
|
is no app-bar title to assert renaming against, so that test now reads the repository
|
|
directly instead) plus one real test bug fixed along the way: two close-together
|
|
waypoints in the "tapping a pin deletes it" test happened to place one pin directly
|
|
under the new floating "Start Route" button in that test's fixed camera geometry, so the
|
|
tap intended for the pin actually hit the button underneath it (confirmed via
|
|
`WidgetController`'s own hit-test-mismatch warning, not assumed) -- reduced to a single
|
|
waypoint, which the camera centres exactly on, clear of the button. `PopupMenuButton`/
|
|
`PopupMenuItem` interactions needed bounded pumps rather than `pumpAndSettle`, same
|
|
reason the map's own tests already avoid it (a live `TileLayer` never goes idle in this
|
|
harness). Also fixed a real, unrelated bug the new `GlassPanel`-wrapped `ListTile` in
|
|
`RoutesListScreen` surfaced immediately: Flutter's own framework assertion that a
|
|
`ListTile` needs a `Material` ancestor to paint its ink splash correctly, which
|
|
`GlassPanel`'s `DecoratedBox` was sitting between it and -- fixed with a
|
|
`Material(type: MaterialType.transparency)` wrapper.
|
|
|
|
**Android emulator verification** (`Medium_Phone_API_35`): confirmed the empty-tooltip
|
|
state, dropping a pin (ripple visible, `FloatingPill` and "Start Route" appearing
|
|
immediately), and the overflow menu opening with all three actions -- all matching the
|
|
mockup and the Design section precisely. The Plan tab's nav-bar pill correctly followed
|
|
the active tab (a mid-transition-animation screenshot briefly looked wrong immediately
|
|
after tapping the tab; a screenshot taken a moment later confirmed it settles correctly
|
|
-- a timing artifact of screenshotting mid-animation, not a real bug).
|