FB-05: closed-loop routes in Route Planner

Add an isClosedLoop toggle to route plans (schema v3 -> v4 migration),
threaded through the domain model, repository distance recomputation,
the overflow menu, and the drawn polyline, so a route can loop back to
its first waypoint.
This commit is contained in:
2026-08-24 16:22:51 -05:00
parent 57bc3c9233
commit 9ac0be6ee5
9 changed files with 359 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
# FB-05 — Closed-loop routes in Route Planner
**Depends on** FB-04 (same file, heavily edited — sequence after it merges) · **Size** M · **Status** Not started
**Depends on** FB-04 (same file, heavily edited — sequence after it merges) · **Size** M · **Status** Done
## Goal
Route planning currently only supports a one-way A→B→C path. Add the ability to close
@@ -409,3 +409,44 @@ _OverflowMenu(
Turn-by-turn following of a closed loop (V3-09, already deferred). Any UI indication of
loop direction/rotation. Road-snapped routing for the closing segment (V3-08) — it stays
a straight line like every other segment in this ticket's scope.
## Outcome
Implemented exactly as designed, with one small addition to the migration step beyond
what the ticket spelled out:
- Added `isClosedLoop` (`BoolColumn`, default `false`) to `RoutePlans` in
`lib/src/data/database.dart`, bumped `schemaVersion` from 3 to 4, and regenerated
`database.g.dart` via `dart run build_runner build`.
- **Deviation**: the migration guard needed to be `if (from >= 3 && from < 4)` rather
than the ticket's plain `if (from < 4)`. A v2 → v4 upgrade runs the existing
`if (from < 3)` branch first, which calls `m.createTable(routePlans)` — and since that
table definition already includes `isClosedLoop` (Drift generates `CREATE TABLE` from
the *current* Dart schema, not a historical snapshot), an unconditional `from < 4`
then tried to `ADD COLUMN isClosedLoop` a second time and hit a duplicate-column
SQLite error. Restricting the `addColumn` step to routes that already had the table
before v4 (`from >= 3`) fixes it; a v1/v2 → v4+ upgrade gets the column for free from
the fresh `createTable` instead. Added a v3-seed migration test
(`test/migration_test.dart`, `'a v3 database (FB-05) gains isClosedLoop defaulting to
false'`) covering exactly this path, plus verifying a v2 → v4+ jump still works via the
existing v2 test.
- Added `isClosedLoop` to the `RoutePlan` domain model, `copyWith`, the
`insertRoutePlan`/`_toRoutePlan` glue, and a new `setRoutePlanClosedLoop` method in
`database.dart`.
- Added `RoutePlanRepository.setClosedLoop` and updated `_recomputeDistance` to append
the first waypoint's coordinates as a closing point when the route is closed and has
2+ waypoints, exactly per the design.
- Extended `_OverflowMenu` in `route_planner_screen.dart` with `canCloseLoop`/
`isClosedLoop`/`onToggleLoop` and a "Close the loop" / "Open the loop" menu item,
wired at the call site.
- Extended the `PolylineLayer`'s point list with the conditional closing point.
- FB-04 had already changed the surrounding `route` access to a guarded `AsyncValue`
pattern (no more `.valueOrNull`) by the time this ticket landed; the new code reads
`route.isClosedLoop` off the same already-resolved `route` value the rest of the
build method uses, so no adjustment to that pattern was needed.
Added migration, repository, and widget tests per the ticket's Tests section (in
`test/migration_test.dart`, `test/route_plan_repository_test.dart`, and
`test/route_planner_screen_test.dart`).
`flutter analyze`: clean (only 4 pre-existing, unrelated `info`-level lints remain).
`flutter test`: all green, **411 tests passing** (up from the 404 baseline).