Turns docs/FEEDBACK.md into five self-contained tickets, each grounded in exact current code and a concrete implementation approach, so they can be handed to independent subagents with no prior context.
194 lines
10 KiB
Markdown
194 lines
10 KiB
Markdown
# FB-02 — Hide the idle Speed panel until recording starts
|
|
|
|
**Depends on** — · **Size** S · **Status** Not started
|
|
|
|
## Goal
|
|
The Record screen's idle (not-recording) state currently shows a live "SPEED" readout
|
|
plus status text before the rider has pressed Start at all. It should show nothing but
|
|
the (live, per FB-01) map and the START control until a ride actually begins.
|
|
|
|
## Context
|
|
Direct user feedback (`docs/FEEDBACK.md`, "Map Page" section):
|
|
|
|
> Remove the "Speed" widget from the map screen when "start" hasn't been pressed yet. It
|
|
> blocks the entire screen, and you can't see the map at all. Speed and other widgets
|
|
> should only appear when recording starts.
|
|
|
|
Today's idle branch — `lib/src/ui/record/record_screen.dart`, inside `build()`'s
|
|
`Stack` (~lines 196-223):
|
|
|
|
```dart
|
|
child: Stack(
|
|
children: [
|
|
if (ui.isIdle)
|
|
Center(
|
|
child: GlassPanel(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
// Current speed, not max: a max figure only moves when you beat
|
|
// it, which reads as a frozen screen while riding steadily.
|
|
// This is the 2.0.1 fix and it must not regress.
|
|
BigStat(
|
|
key: const Key('speed'),
|
|
label: 'SPEED',
|
|
value: speedValue,
|
|
unit: speedUnit,
|
|
scale: mountedMode ? mountedTextScale : 1.0,
|
|
),
|
|
const SizedBox(height: 16),
|
|
// A resting state, not a zeroed ride -- otherwise the screen
|
|
// looks like a recording that is going nowhere.
|
|
const StatRow(label: 'Status', value: 'Ready'),
|
|
const StatRow(label: 'Last ride', value: 'See Rides'),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
else
|
|
Positioned.fill(
|
|
bottom: _controlBarHeight(mountedMode),
|
|
child: HudEditOverlay(
|
|
metricBuilder: (context, metric) =>
|
|
_HudMetricValue(metric: metric, ui: ui, units: units),
|
|
),
|
|
),
|
|
...
|
|
```
|
|
|
|
`ui.isIdle` is `trip == null` (`RecordUiState.isIdle`, ~line 42). The `GlassPanel` sizes
|
|
to its content (`mainAxisSize: MainAxisSize.min`) so it isn't literally full-screen, but
|
|
it's centered, opaque-ish (`GlassPanel` is a near-opaque translucent fill per its own
|
|
doc comment elsewhere in this codebase), and shows a real live speed figure
|
|
(`_liveSpeed`, updated every GPS tick via `liveTelemetryProvider` — see `initState`
|
|
~lines 86-88) before any ride exists — which is exactly what the feedback is describing
|
|
as "blocking the screen" and confusing to see before pressing Start.
|
|
|
|
## Design
|
|
Remove the entire `if (ui.isIdle) ... else ...` branching for HUD content — always
|
|
render the `HudEditOverlay` branch, and have the idle state simply show nothing there
|
|
(the HUD overlay area is empty when there's no trip, since there's nothing to edit/no
|
|
metrics to place — confirm `HudEditOverlay` degrades gracefully with an idle
|
|
`RecordUiState`, or gate it explicitly: only mount `HudEditOverlay` when `!ui.isIdle`,
|
|
and render an empty `SizedBox.shrink()` (or nothing at all) for the idle case, so the
|
|
full-bleed live map (per FB-01) is the entire idle screen behind the START control bar.
|
|
|
|
Concretely:
|
|
|
|
```dart
|
|
child: Stack(
|
|
children: [
|
|
if (!ui.isIdle)
|
|
Positioned.fill(
|
|
bottom: _controlBarHeight(mountedMode),
|
|
child: HudEditOverlay(
|
|
metricBuilder: (context, metric) =>
|
|
_HudMetricValue(metric: metric, ui: ui, units: units),
|
|
),
|
|
),
|
|
if (ui.isPaused)
|
|
...
|
|
```
|
|
|
|
(i.e. drop the idle `Center(child: GlassPanel(...))` block entirely — no replacement
|
|
content, not even a "Ready" label. The now-live background map, per FB-01, is the idle
|
|
screen's entire content above the control bar.)
|
|
|
|
`speedValue`/`speedUnit` (`formatSpeedParts(_liveSpeed, unit: units)`, ~line 176) become
|
|
unused once the idle panel is removed — check whether `_HudMetricValue`'s own
|
|
`HudMetric.speed` case (`formatSpeedParts(ui.currentSpeedKmh, unit: units).$1` at
|
|
~line 310) already computes this independently (it does — `ui.currentSpeedKmh` is
|
|
`_liveSpeed`), so the `final (speedValue, speedUnit) = ...` line in `build()` should
|
|
simply be deleted rather than left as dead code. Run `flutter analyze` to confirm no
|
|
other lingering unused-variable warnings from this removal.
|
|
|
|
`BigStat`/`StatRow` imports in `record_screen.dart` (from `../components/stats.dart`)
|
|
may become unused if nothing else in this file still uses them — check before removing
|
|
the import; `confirmDialog` (also from `stats.dart`) is used elsewhere in this file for
|
|
the discard confirmation, so the import itself likely stays, just drop `BigStat`/
|
|
`StatRow` specifically if nothing else references them.
|
|
|
|
## Implementation
|
|
1. Remove the idle `Center(child: GlassPanel(...))` block from `record_screen.dart`'s
|
|
`build()`.
|
|
2. Gate `HudEditOverlay` on `!ui.isIdle` (it was previously the `else` branch of the
|
|
idle check — now it's the only branch, wrapped in its own `if`).
|
|
3. Delete the now-unused `final (speedValue, speedUnit) = formatSpeedParts(...)` line.
|
|
4. Run `flutter analyze` and remove any import that's now unused as a result.
|
|
|
|
## Acceptance criteria
|
|
- [ ] Idle state (no active trip) shows no Speed/Status/"Last ride" content at all —
|
|
just the live background map and the control bar with only "START RECORDING"
|
|
visible (unchanged from today).
|
|
- [ ] The moment a trip starts recording, the full HUD (`HudEditOverlay` with the
|
|
default-visible metrics: Speed, Avg Speed, Distance, Elapsed Time) appears exactly
|
|
as it does today — this ticket only changes the idle state, not recording/paused.
|
|
- [ ] `flutter analyze` clean (no unused-variable/import warnings from the removal).
|
|
- [ ] `flutter test` green, test count only goes up (net effect after the test
|
|
migrations below should still be positive or flat, never negative).
|
|
|
|
## Tests
|
|
Several existing tests assert against the idle Speed panel specifically and must be
|
|
migrated — not deleted — to seed an active recording and assert against the HUD's own
|
|
`Speed` widget instead, since the underlying regressions they guard (black-on-black
|
|
text, mounted-mode contrast/scale) are real risks that still apply once the same figure
|
|
lives inside `_HudMetricValue` instead of the idle `BigStat`. The HUD's per-metric
|
|
widgets are keyed `ValueKey(entry.key)` where `entry.key` is the `HudMetric`
|
|
(`lib/src/ui/components/hud_edit_overlay.dart` ~line 63) — use
|
|
`find.byKey(const ValueKey(HudMetric.speed))` to locate the speed widget in a recording
|
|
state, then descend into it the same way the old tests descended into `Key('speed')`.
|
|
|
|
In `test/widget_test.dart`:
|
|
|
|
- **`'idle shows Ready and only START'`** (~line 126): remove the
|
|
`expect(find.text('Ready'), findsOneWidget);` assertion (the "Ready"/"Status" text no
|
|
longer exists at all); keep the START/PAUSE/STOP key assertions and the
|
|
`find.text('Distance')` findsNothing check — both remain valid (this test's title may
|
|
want updating to `'idle shows only START'`).
|
|
- **`'the headline is live speed, not max speed'`** (~line 138): change to seed a
|
|
recording trip first (`await repo.startTrip(1000); await pumpLive(tester,
|
|
host(const RecordScreen(), map: false));` — see the identical pattern already used at
|
|
~line 205-206 in `'tapping PAUSE and then STOP...'`), then assert
|
|
`find.text('SPEED')` findsOneWidget / `find.text('MAX SPEED')` findsNothing exactly as
|
|
before (Max Speed is not one of the four default-visible metrics —
|
|
`HudWidgetLayout.defaultFor`'s `visible: index < columns` with `columns = 4` and
|
|
`HudMetric.values` ordered `speed, avgSpeed, distance, elapsedTime, maxSpeed, ...` —
|
|
so this assertion still holds true in the recording state).
|
|
- **`'text is legible against the dark ground'`** (~line 235): seed a recording trip the
|
|
same way, then replace
|
|
`find.descendant(of: find.byKey(const Key('speed')), matching: find.text('0'))` with
|
|
`find.descendant(of: find.byKey(const ValueKey(HudMetric.speed)), matching:
|
|
find.text('0'))`. Same downstream assertions (`colour != ripprBackground`,
|
|
`colour != Colors.black`) unchanged.
|
|
- **`'the mounted theme is high-contrast and text scales up (V3-05)'`** (~line 380) and
|
|
**`'the mounted-mode speed digit is legible against GlassPanel's own translucent
|
|
surface...'`** (~line 400): both currently pump `RecordScreen()` idle with
|
|
`mountedMode: true` and inspect `Key('speed')`. Migrate both to seed a recording trip
|
|
first, then use `ValueKey(HudMetric.speed)` the same way. Confirm `_HudMetricValue`'s
|
|
value `Text` actually honors `mountedMode`'s scale/theme the same way the old idle
|
|
`BigStat` did (check `_HudMetricValue.build()` — if it currently has no mounted-mode
|
|
awareness at all, that's a **pre-existing gap this migration would newly expose**, not
|
|
something to paper over: if the mounted-mode font-size/contrast assertions fail
|
|
against the HUD widget, that's a real, separate bug worth calling out plainly in this
|
|
ticket's Outcome section rather than weakening the assertion to make it pass.
|
|
- **`'HUD widgets render over the map, not replacing it (UI-05)'`** (~line 219) already
|
|
seeds a recording trip and asserts `find.text('SPEED')`/`find.text('DISTANCE')` — no
|
|
change needed, but worth a quick sanity check that it still passes unmodified.
|
|
|
|
## Risks
|
|
- The mounted-mode migration above may surface that `_HudMetricValue` never actually
|
|
had mounted-mode-aware styling (it was only ever exercised via the idle `BigStat`
|
|
before now) — if so, this ticket's Outcome section must say so explicitly rather than
|
|
silently deleting or weakening the assertion. Fixing that gap (if real) is reasonable
|
|
to fold into this ticket since it's directly caused by this change, but keep the fix
|
|
minimal (reuse whatever scale/theme mechanism `BigStat` already used) rather than
|
|
redesigning `_HudMetricValue`.
|
|
|
|
## Out of scope
|
|
The HUD grid/drag-resize/text-auto-fit rework (FB-03) — this ticket only removes idle
|
|
content, it does not touch how the recording-state HUD widgets are laid out or sized.
|
|
The live-map-while-idle behavior itself (FB-01) — this ticket assumes it lands
|
|
separately; if FB-01 hasn't merged yet, the idle screen will just show today's
|
|
`(0,0)`/zoom-2 map, which is still a strict improvement over a blocking Speed panel.
|