T19/T21: map and export, completing Phase 4
flutter_map with the same OSM tiles and no-API-key reasoning that chose osmdroid. All four load-bearing behaviours ported and, unlike the native app's map, tested: one polyline per segment so a pause is a real gap, render-only decimation, the zoom clamp at OSM's max tile zoom with a short-ride fallback, and a real user agent. Speed colouring is bucketed per run rather than per-vertex, since neither osmdroid nor flutter_map makes per-vertex paint reasonable. Export via share_plus, which also handles the iPad popover anchor a naive port forgets. It passes the raw stored points, never the map's decimated path. 164 tests passing, analyze clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -576,3 +576,88 @@ Realistically this machine needs headroom freed outside the dev tooling before P
|
||||
|
||||
Next: **Phase 4, the UI** — six screens, and the first widget tests this project has ever
|
||||
had.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — UI · **complete**
|
||||
|
||||
**T15** shell (theme, `go_router`, shared components) · **T16** record screen · **T17**
|
||||
trips list · **T18** trip detail with stats and charts · **T19** map · **T20**
|
||||
rename/delete/merge · **T21** export · **T23** widget tests, brought forward.
|
||||
|
||||
**164 tests passing, analyze clean.**
|
||||
|
||||
### The theme's load-bearing detail, made structural
|
||||
|
||||
Compose's `Surface` set `LocalContentColor`; removing it once made a 64 sp speed figure
|
||||
render black-on-black, and **no test caught it — only a screenshot did**. The Dart theme
|
||||
sets `bodyColor`/`displayColor` on `TextTheme` explicitly rather than relying on a
|
||||
wrapping widget, so the failure cannot recur by someone deleting a container. `BigStat`
|
||||
also names its colour directly, and a widget test asserts that colour differs from the
|
||||
ground.
|
||||
|
||||
### The widget tests immediately earned their keep
|
||||
|
||||
**A real layout bug, first run:** with a ride active the record screen grows to six stat
|
||||
rows and `RenderFlex overflowed by 20 pixels`. Compose *clips this silently*, so the same
|
||||
bug may well be latent in the native app and simply invisible. Fixed by making the screen
|
||||
scrollable while still centring when there is room — which matters more here than usual,
|
||||
because 72 dp glove-sized controls make the content genuinely tall.
|
||||
|
||||
### Two Flutter-testing traps, both costly
|
||||
|
||||
**`pumpAndSettle` never settles against a repeating timer.** The elapsed clock ticks every
|
||||
second, so the first widget-test run sat at the framework's 10-minute timeout — for
|
||||
*every* test. Two fixes: the ticker now runs **only while a ride is active** (better
|
||||
behaviour regardless — an idle screen has no clock to advance), and tests that do have a
|
||||
live ride use explicit `pump()` calls.
|
||||
|
||||
**`flutter_test` asserts no `Timer` is pending after disposal**, which Drift trips: it
|
||||
keeps a stream query alive briefly after its last listener leaves so re-subscribing is
|
||||
cheap. Tests now run through a `screenTest` wrapper that removes the tree and pumps past
|
||||
that window. Run time went from *timeout* to **two seconds**.
|
||||
|
||||
> And again: a killed background job reports exit code 0. Twice during this phase a
|
||||
> "completed" run had actually been terminated. Always re-read the log.
|
||||
|
||||
### Map
|
||||
|
||||
`flutter_map`, same OSM raster tiles and same no-API-key reasoning that chose osmdroid.
|
||||
All four load-bearing behaviours carried over and now **tested**, which the native app's
|
||||
map never was:
|
||||
|
||||
- one polyline per segment, so a pause is a visible gap — the test puts two segments a
|
||||
degree apart and asserts no polyline straddles it
|
||||
- decimation **render-only**; a test asserts vertices drop while endpoints survive
|
||||
- the **zoom clamp** at OSM's max tile zoom of 19, plus a `shortRideZoom` fallback for
|
||||
degenerate bounds — this is the v2.0 empty-grid bug, now pinned by a test
|
||||
- a real user agent, or the tile servers return 403
|
||||
|
||||
Speed colouring is bucketed into one polyline per run rather than per-vertex paint —
|
||||
`PolyChromaticPaintList` was fiddly in osmdroid and flutter_map has no equivalent either.
|
||||
**Still unvalidatable:** no simulator produces velocity, so every path renders in one
|
||||
colour until a real ride.
|
||||
|
||||
### Export
|
||||
|
||||
`share_plus` replaces `FileProvider` + `ACTION_SEND`, and handles the iOS popover anchor
|
||||
an iPad needs. Files are written to the temporary directory — they are a transfer
|
||||
artefact, not storage. The export deliberately passes the **raw stored points**, never
|
||||
the map's decimated path.
|
||||
|
||||
---
|
||||
|
||||
## Remaining
|
||||
|
||||
Phases 0–4 are complete. What is left is verification and cutover:
|
||||
|
||||
- **T22** uploader + config (the last piece of parity; still no UI, exactly as today)
|
||||
- **T24** integration tests on both platforms — needs the emulator, so check disk first
|
||||
- **T25** the real-ride checklist on **both** platforms. Nothing above substitutes for it:
|
||||
neither simulator produces velocity, so max speed, moving time and speed colouring are
|
||||
all still unverified.
|
||||
- **T26** iOS release readiness · **T27** parity audit, including switching the
|
||||
applicationId from `com.rippr.port` back to `com.rippr`
|
||||
|
||||
**Known parity gap so far:** notification actions (Pause/Resume in the shade), lost with
|
||||
`flutter_foreground_task`.
|
||||
|
||||
Reference in New Issue
Block a user