v4 was always just the server-dependent programme (group rides, accounts, paid backup) plus V3-17. Renaming it to ROADMAP since it's about to become the home for an incoming list of bugs and features that need triage ahead of the next v3-style ticket batch -- that triage takes priority over the existing server-dependent items, none of which are blocking.
4.1 KiB
V3-02 — Settings screen
Phase Foundations · Depends on nothing (pairs with V3-01, V3-03) · Size S · Status Done
Goal
One place for the preferences that currently have nowhere to live.
Context
Config already holds uploadEndpoint, deviceId and mapEnabled, but there is no UI
for any of them. The map toggle sits on trip detail because a single switch did not justify
a screen. V3-01 and V3-03 both add preferences, which finally does justify one.
The upload endpoint has never had UI, in the native app or the port. This is where it stops being a code-only setting.
Design
Reached from the record screen. Sections:
- Recording — default activity (V3-01), units (V3-03)
- Map — render maps on trip detail; later, live map (V3-04)
- Sync — upload endpoint, with the device id shown read-only and copyable
- About — version, a link to the privacy policy, licences
Keep it plain. This is not a screen anyone should spend time in.
Implementation
SettingsScreen+ ago_routerroute- Make
Configreactive — it is currently read once into a provider; settings need writes to propagate. AConfigNotifierovershared_preferences. - Move the map toggle off trip detail
- Endpoint field validates it parses as a URL and is http(s)
Acceptance criteria
- Every
Configvalue is viewable and editable - Changing the map toggle takes effect without an app restart
- An invalid endpoint is rejected with a readable message, not silently stored
- Device id is copyable — it is the only way to identify this install to a server
Tests
- Widget: each control renders and writes through to
Config - Widget: invalid URL rejected
- Changing the map toggle rebuilds trip detail
Risks
Config is currently loaded once at startup into a StateProvider. Making it writable
without introducing a second source of truth is the only subtle part.
Out of scope
Account settings (ROADMAP). Theme selection (V3-16).
Outcome
"Move the map toggle off trip detail" (implementation step 3) turned out to be moot —
mapEnabledProvider already existed and drove RideMap's visibility, but no widget
anywhere ever offered a control to change it. There was nothing to move. Settings adds
the first one.
No ConfigNotifier was built — see V3-03's outcome for why the existing
mapEnabledProvider-style StateProvider pattern covers reactivity without it, and
unitSystemProvider was added the same way, in providers.dart, ahead of this ticket.
Two implementation-step items shipped differently than drafted, both to avoid adding a
dependency disproportionate to an S-sized settings screen:
- No
package_info_plus. The About section's version string is a static literal matchingpubspec.yaml's1.0.0+1, not a live package lookup. Fine today; would need revisiting if the version ever needs to be authoritative from inside the running app rather than copied by hand. - No privacy-policy link. None is published yet (see
docs/LAUNCH.md) and linking to one that does not exist would be worse than omitting it. Shows a plain note instead. Licences are still free: Flutter's built-inshowLicensePageneeded no new dependency.
Endpoint validation accepts http:// and https:// with a non-empty host, and treats
an empty field as valid — that is how upload gets disabled, not an error state. A
non-empty invalid value is rejected with inline errorText and never reaches Config;
proven by a widget test that types garbage, taps Save, and asserts Config.uploadEndpoint
is still empty afterwards.
uploaderProvider needed an explicit ref.invalidate() after saving the endpoint, for
the identical reason unitSystemProvider needed its own StateProvider rather than
reading through configProvider directly — a Config write never changes the Config
instance Riverpod is watching, so nothing downstream rebuilds unless told to.
10 tests: 9 in settings_screen_test.dart, 1 confirming the record screen's settings
button is genuinely wired (not just present) in widget_test.dart.