Adaptive running coach for iOS and Android: personal plans from 5K to 50K that rewrite themselves after every run.
Live demo: https://www.freelancerportfoliohub.com/jameslee/projects/stridecoach/index.html
This repository contains the Stride app, the training engine behind it and a small API server.
- App (repo root) is a Flutter app for iOS, Android and the web with five tabs: Today, Plan, Pace zones, Race prep and Explore, plus a full-screen Live run view. Explore carries what used to be the marketing site: the race-time predictor, the plan catalog, how it works, membership, stories and the newsletter.
- Engine (
packages/stride_core/) is a pure-Dart package with the pace math, the plan builder and the adaptive coach. It has no Flutter dependency, so the app and the server share exactly the same code. - Server (
server/) is a Dart service that answersPOST /api/predictandPOST /api/newsletterand hosts the Flutter web build.
Pace zones, finish predictions, course splits and every plan session come from stride_core, so the numbers in
the app and the API always agree.
- Adaptive plans from the mile to 50K, plus base building and return-to-run. Plans are periodized into Base, Build, Peak and Taper weeks, grow volume no more than 10% a week and cut back every fourth week.
- Rewritten after every run. Each run is compared with its session (pace against the expected average, effort rating, heart rate, distance). Strong sessions nudge zones faster, tough ones ease the next hard day and trim volume, races re-anchor fitness, missed days are reconciled, and every change comes with a plain-English coach note.
- Run logging, either recorded live or entered afterwards, with a training log of weekly volume, runs and coach notes.
- Move sessions between days: drag a session in the Plan tab and the week rebalances so hard days never stack.
- Change goals mid-plan: weeks already run are kept and the rest rebuild around the new race.
- Five personal pace zones derived from 5K pace, with time in zone from completed sessions.
- Live run with rolling current pace, zone position, interval progress and a pause-aware running clock. On devices without a GPS feed it runs on a clearly labelled simulated feed.
- Race prep with predicted finish, elevation profile, taper checklist and grade-adjusted mile splits.
- Race-time predictor: a finish range from a recent 5K, weekly availability and plan length, in the app and
at
POST /api/predict. - Newsletter sign-up backed by a provider-agnostic service (
POST /api/newsletter).
| Layer | Tools |
|---|---|
| App | Flutter (Material 3), Dart, shared_preferences, http |
| Engine | Pure Dart package (stride_core) |
| API | Dart, shelf, shelf_router, shelf_static |
| Tooling | flutter analyze / dart analyze, flutter test / dart test, Docker |
Requires the Flutter SDK (which includes Dart).
flutter pub get
flutter run # pick an iOS Simulator, Android emulator or ChromeThe app starts with a demo runner five weeks into a half-marathon block. Explore → ⋮ → Reset to demo runner brings it back at any time.
API server with the web app:
flutter build web
cd server
dart pub get
WEB_ROOT=../build/web dart run bin/server.dart # http://localhost:8080The native apps reach the API through --dart-define=STRIDE_API_URL=http://<host>:8080/; the web build uses its
own origin.
Copy .env.example and export the values for the server. Without the newsletter variables, sign-ups are logged to
the console.
| Variable | Purpose |
|---|---|
PORT |
Port the server listens on (default 8080) |
WEB_ROOT |
Directory with the Flutter web build (public) |
NEWSLETTER_API_URL |
Subscribe endpoint of the email provider |
NEWSLETTER_API_KEY |
Bearer token for that endpoint |
flutter analyze && flutter test # app: controller, screens, live run, explore
(cd packages/stride_core && dart test) # engine: pace math, plan builder, adaptive coach
(cd server && dart test) # API: predict, newsletter, static hostingThe Dockerfile builds the web app, compiles the server to a native executable and ships both in a minimal
image that listens on $PORT. railway.toml points Railway at it with /api/health as the health check.
docker build -t stride .
docker run -p 8080:8080 stride.
├── lib/
│ ├── main.dart, app.dart # bootstrap, theme, home shell
│ ├── screens/ # today, plan, zones, race prep, live run, log run, history, start plan
│ │ └── explore/ # predictor, plan catalog, how it works, membership
│ ├── widgets/ # cards, chips, week strip, workout bars, zone scale, charts, marketing blocks
│ ├── state/ # StrideController (ChangeNotifier) and persistence
│ ├── services/ # newsletter API client, simulated GPS feed
│ └── theme/ # design tokens and Material theme
├── packages/stride_core/
│ ├── lib/src/pace.dart # zones, Riegel predictions, pace formatting, course splits
│ ├── lib/src/engine/ # plan builder, session factory, adaptive coach, week balancer, goal changes
│ ├── lib/src/models/ # goals, courses, weeks, sessions, run logs, runner
│ ├── lib/src/content/ # plan catalog, pricing, FAQs, coaches, copy, demo runner
│ └── lib/src/run_tracker.dart
├── server/ # shelf API + static hosting for the web build
├── assets/ # fonts and images
├── web/, android/, ios/ # platform runners
└── Dockerfile, railway.toml
