Directory map
File counts are tracked files (git ls-files) at d1b602a. Generated and ignored folders are
listed separately at the end.
Top level
Section titled “Top level”| Path | Files | What it is |
|---|---|---|
lib/ |
110 | The app. Everything that ships as Dart. Detail below. |
test/ |
91 | Unit and widget tests, mirroring lib/, plus the shared harness, fixtures and the screenshot renderer. Detail below. |
integration_test/ |
1 | full_race_test.dart: register, race the steady bot over 100 m with simulated GPS, check History and Profile. Runs on an emulator against a real backend via scripts/integration.sh. |
widgetbook/ |
38 | A second Flutter app: the component catalog (Storybook for Flutter). Path dependency on this package; never shipped. Detail below. |
android/ |
48 | The native Android shell that hosts the Flutter engine. Detail below. |
ios/ |
49 | The native iOS shell. Detail below. |
assets/ |
9 | brand/ (4: icon, adaptive icon, splash icon, logo.svg) and fonts/ (5: Plus Jakarta Sans regular and italic, Space Grotesk, two OFL licence files). Declared in app/pubspec.yaml:55-63. |
docs/ |
55 | Project docs inside the repo. Detail below. |
env/ |
3 | dev.json, staging.json, prod.json: the --dart-define values (API_BASE_URL, DEV_TOOLS, FAKE_GPS) for --dart-define-from-file. |
scripts/ |
1 | integration.sh: boots the backend, resets and seeds the dev database, runs the integration test. |
tool/ |
1 | check_coverage.dart: reads coverage/lcov.info, fails below the minimum. make coverage passes 70 (app/Makefile:23); the script’s own default is 60. Excludes lib/dev/. |
.github/ |
1 | workflows/ci.yml: jobs check (format, analyze, test, coverage), build-android (debug APK), storybook (Widgetbook analyze + web build), and integration, which runs only on manual workflow_dispatch. |
Root files:
| File | What it is |
|---|---|
pubspec.yaml |
Package manifest: dependencies, fonts, icon and splash config. The package.json / mix.exs of the app. |
pubspec.lock |
Resolved versions; committed because this is an app. |
analysis_options.yaml |
Lint config: flutter_lints plus a few rules; excludes the platform folders. |
Makefile |
check, format, analyze, test, coverage, integration, run-dev, release-android, release-ios, apk, icons, and the storybook* targets. See build and release. |
.tool-versions |
flutter 3.47.2-stable for asdf. |
.metadata |
Written by flutter create; tracks which platforms were generated. Do not edit. |
CLAUDE.md, README.md |
Agent instructions and the human README (environments, release builds, the manual on-device GPS check). |
.gitignore |
Note it ignores /linux/, /macos/, /web/, /windows/: only Android and iOS are built. |
lib/├── main.dart 1├── app/ 4├── core/ 5 (4 + config/1)├── data/ 33 (api 2, models 13, repositories 10, socket 5, storage 3)├── design/ 21 (2 + components 15 + components/race 4)├── dev/ 4├── features/ 31 (14 feature folders)└── tracking/ 11 (8 + upload/3)The architecture overview shows how these depend on each other.
main.dart (1)
Section titled “main.dart (1)”Bootstrap only: Flutter binding, error handlers, the foreground-task communication port, then
runApp(ProviderScope(child: FootraceApp())) (app/lib/main.dart:9-21).
app/ (4)
Section titled “app/ (4)”| File | What |
|---|---|
app.dart |
FootraceApp, the root ConsumerWidget: MaterialApp.router with the theme and router, and keeps the socket lifecycle alive (app/lib/app/app.dart:9-21). |
router.dart |
routerProvider: every GoRoute, the four-tab StatefulShellRoute, and the auth redirect (app/lib/app/router.dart:27-147). |
routes.dart |
AppRoutes, path constants and builders like AppRoutes.race(id) (app/lib/app/routes.dart:2-21). |
theme.dart |
footraceTheme(): Material ThemeData built from the design tokens, with FrTheme attached (app/lib/app/theme.dart:12). |
Page: app shell.
core/ (5)
Section titled “core/ (5)”| File | What |
|---|---|
config/app_config.dart |
AppConfig from --dart-define, the derived socket URL, appConfigProvider. The only reader of the defines. |
clock.dart |
typedef Clock and clockProvider, so tests control “now”. |
logging.dart |
The log logger, installErrorHandlers(), LoggingProviderObserver. |
units.dart |
Units enum, unitsProvider, UnitsFormatter (every distance, pace and duration string). |
relative_time.dart |
“5 min ago”-style formatting for the Activity feed. |
data/ (33)
Section titled “data/ (33)”| Folder | Files | What |
|---|---|---|
api/ |
2 | api_client.dart: buildDio (bearer header, 401 handling, error normalisation), guard, dioProvider. api_exception.dart: ApiException. |
models/ |
13 | Hand-written immutable models with fromJson/toJson: user, auth_response, race_type, matched_race, race_snapshot, race_result, race_history_item, activity_item, leaderboard, lobby_entry, public_profile, user_settings, user_stats. |
repositories/ |
10 | One per backend area: auth, user, profile, race_type, matchmaking, lobby, race, race_history, activity, leaderboard. Each is an abstract class, an Api... implementation and a provider. The only code that knows URLs. |
socket/ |
5 | channel_transport.dart (the narrow SocketTransport/ChannelTransport interfaces), phoenix_transport.dart (implementation over phoenix_socket), race_channel.dart, waiting_channel.dart (typed channel clients), socket_service.dart (providers). |
storage/ |
3 | token_store.dart (JWT in Keychain/Keystore), user_cache.dart (last user in SharedPreferences), active_race_store.dart (the race to resume after a kill). Each has an in-memory twin for tests. |
Pages: data layer, realtime.
design/ (21)
Section titled “design/ (21)”| Path | Files | What |
|---|---|---|
tokens.dart |
1 | FrColors, FrSpacing, FrRadii, FrShadows, FrFonts: every visual constant, named by role. |
typography.dart |
1 | The TextTheme, the FrTheme theme extension, and context.fr (app/lib/design/typography.dart:309-313). |
components/ |
15 | Fr* widgets: avatar, button, card, eyebrow, icon button, icon tile, list row, metric tile, navigation bar, section header, segmented control, stat, stat group, tag, plus the components.dart barrel export. |
components/race/ |
4 | Race-screen pieces: head-to-head, live pill, slide-to-confirm, and the race.dart barrel. |
design/ imports nothing else from lib/. Page: design system.
dev/ (4)
Section titled “dev/ (4)”Debug-only tooling, reachable at /dev only when DEV_TOOLS is true
(app/lib/app/router.dart:117-121), and excluded from the coverage gate.
| File | What |
|---|---|
gps_diagnostics_screen.dart |
The GPS proof-of-concept screen: start/stop the service, see fixes from both isolates. |
tracking_diagnostics.dart |
trackingDiagnosticsProvider: gap and fix statistics fed by the service’s batches. |
sim_gps_controller.dart |
simGpsProvider: state of the simulated runner. |
sim_gps_overlay.dart |
Draggable control panel for the simulated GPS, shown on the race screen when FAKE_GPS is on. |
features/ (31)
Section titled “features/ (31)”One folder per screen or flow, holding widgets and the notifier that owns their state. The features page has the full table of screens, notifiers and routes.
| Folder | Files | Contents |
|---|---|---|
auth/ |
5 | auth_notifier.dart (session owner, authProvider, authStatusProvider), auth_state.dart, auth_form.dart, login_screen.dart, register_screen.dart |
home/ |
1 | home_shell.dart: the bottom-tab scaffold |
race_tab/ |
3 | The Race tab (pick a distance): race_tab.dart, race_type_card.dart, race_types_notifier.dart |
activity/ |
2 | Activity feed tab and its notifier |
history/ |
2 | History tab and its notifier |
profile/ |
4 | profile_tab.dart, profile_widgets.dart, settings_notifier.dart, opponent_profile_screen.dart (with its two providers) |
leaderboard/ |
2 | Screen and notifier, plus the filter StateProvider |
lobby/ |
2 | Open challenges: screen and notifier |
queue/ |
2 | Matchmaking: queue_notifier.dart (sealed QueueState), queue_screen.dart |
race/ |
4 | race_notifier.dart, race_screen.dart, result_screen.dart (with raceResultProvider), local_distance.dart |
permissions/ |
1 | PermissionsSheet (the explainer shown before the first queue join) and PermissionBanner |
shared/ |
3 | splash_screen.dart, message_state.dart (empty/error placeholder), coming_soon_screen.dart (also the 404 page) |
tracking/ (11)
Section titled “tracking/ (11)”| File | What |
|---|---|
location_service.dart |
LocationService (UI side: permissions, start/stop the service) and LocationTaskHandler (runs inside the service isolate). The load-bearing file. |
location_source.dart |
LocationSource interface and RealLocationSource over geolocator. |
simulated_location_source.dart |
The fake runner used with FAKE_GPS. |
gps_fix.dart |
GpsFix and TrackingState. |
geo.dart |
Great-circle distance helpers. |
location_provider.dart |
locationProvider for the diagnostics screen. |
race_tracker.dart |
RaceTracker: what a race needs from the tracker; raceTrackerProvider. |
permission_gate.dart |
PermissionGate notifier: what is still missing, asked one step at a time. |
upload/fix_queue.dart |
Durable per-race queue of fixes on sembast. |
upload/position_uploader.dart |
Drains the queue to POST /api/races/:id/positions in batches of 50 with backoff. |
upload/race_upload_target.dart |
The keys the UI isolate saves for the service: race id, base URL, token, queue path. |
Page: tracking.
test/ mirrors lib/. 91 tracked files:
| Path | Files | What |
|---|---|---|
app_test.dart |
1 | Boots the whole app on fakes. |
core/ |
2 | app_config_test.dart, units_test.dart |
data/ |
13 | One test per repository, lobby_entry_test.dart, fixture_contract_test.dart, and socket/ (3) |
design/ |
19 | theme_test.dart and 18 component tests |
features/ |
20 | Notifier and widget tests per feature |
tracking/ |
5 | geo, gps_fix, simulated source, and upload/ (2: fix queue, uploader) |
support/ |
5 | The harness: fakes.dart (testApp, testOverrides, every fake), fake_api_client.dart, fake_socket.dart, fixtures.dart, json_contract.dart |
fixtures/ |
23 | 22 real backend payloads as JSON plus a README.md on the backend’s inconsistencies |
screenshots/ |
3 | Not a test suite: a renderer that writes 1080x2400 PNGs of every screen when FR_SCREENSHOTS is set |
Page: testing.
widgetbook/
Section titled “widgetbook/”| Path | Files | What |
|---|---|---|
lib/ |
7 | main.dart, foundations.dart (tokens), components.dart and the design_*.dart files (use cases with knobs) |
android/ |
19 | Its own Android shell, for make storybook-phone |
web/ |
7 | Web shell, for make storybook (served on port 8790) |
| root | 5 | Its own pubspec.yaml, pubspec.lock, analysis_options.yaml, .metadata, .gitignore |
android/ and ios/
Section titled “android/ and ios/”Both are standard flutter create shells. Almost everything in them is boilerplate; the parts
that are Footrace-specific are permissions, the foreground service and the app id.
| Path | Files | What matters |
|---|---|---|
android/app/src/main/AndroidManifest.xml |
1 | Location, background location, foreground-service, notification and wake-lock permissions; the flutter_foreground_task service with foregroundServiceType="location" (app/android/app/src/main/AndroidManifest.xml:2-9, :36-40) |
android/app/src/main/kotlin/.../MainActivity.kt |
1 | class MainActivity : FlutterActivity(), nothing else |
android/app/src/main/res/ |
35 | Launcher icons, splash drawables, launch themes |
android/app/src/debug/, src/profile/ |
2 | Debug and profile manifests (internet permission for the tool) |
android/app/build.gradle.kts, proguard-rules.pro |
2 | App id, SDK levels, signing, release shrinking |
android/ root |
7 | Gradle settings and wrapper properties, key.properties.example |
ios/Runner/AppDelegate.swift, SceneDelegate.swift |
2 | FlutterAppDelegate and plugin registration |
ios/Runner/Info.plist |
1 | Location usage strings, UIBackgroundModes location and fetch (app/ios/Runner/Info.plist:69-77) |
ios/Runner/Base.lproj/ |
2 | Main.storyboard (a FlutterViewController), LaunchScreen.storyboard |
ios/Runner/Assets.xcassets/ |
29 | App icon set and launch image |
ios/Runner.xcodeproj, Runner.xcworkspace, Flutter/, RunnerTests/ |
12 | Xcode project, workspace, the Flutter xcconfigs, the template XCTest target |
ios/ root |
2 | .gitignore, ExportOptions.plist.example (copy for make release-ios) |
At d1b602a there is no tracked ios/Podfile; the local checkout has an untracked one and
uncommitted changes to the Xcode project, which suggests CocoaPods was run locally after the
commit. Page: build and release.
| Path | Files | What |
|---|---|---|
design/stitch/ |
22 | The Stitch mockups the UI is built from: README.md (settles tokens where exports disagree), screens/ (home, lobby, racing as HTML + PNG), components/ (15 PNG crops) |
use-cases/ |
13 | One markdown file per manual flow (register, queue, lobby, bot race win/lose, abort, kill app, locked phone, lose network, permissions denied, history, units) plus a README |
qa/ |
2 | device-checklist.md (real-device pass) and report-template.md |
screenshots/ |
18 | Rendered screens committed per story (elb-1285/, elb-1683/, elb-1684/) and a README on how they are made |
Generated and ignored
Section titled “Generated and ignored”Present in a working checkout, never edited by hand:
| Path | What |
|---|---|
build/ |
Build output for every target |
.dart_tool/ |
Package resolution and tool caches (like _build/ or node_modules/.cache) |
.flutter-plugins-dependencies |
Which plugins each platform needs; written by flutter pub get |
linux/, macos/, web/, windows/ |
Exist locally from flutter create but are git-ignored and not built |
coverage/ |
lcov.info from make coverage |
.idea/, *.iml |
IDE files (footrace_flutter.iml and android/footrace_flutter_android.iml are tracked anyway) |
ios/Pods/, android/.gradle/, GeneratedPluginRegistrant.java |
Native dependency and plugin-registration output |