๐ฑ Frontend Overview
Cross-Platform Flutter Application
:::tip TL;DR
- One Flutter codebase โ iOS, Android, Web
- Provider for state โข Firebase SDK for auth/storage/functions/messaging
- Videos are uploaded through
requestUploadPathas a single file today; client-side chunking is deferred - All Cloud Function calls go to
europe-west1โ don't forget the region - 17 Ace components + semantic design tokens (colors, typography, spacing, motion) โ see Design System
- New users see sample data until their first upload โ see Key Features :::
The AceSense frontend is a cross-platform application built with Flutter, supporting iOS, Android, and Web from a single codebase.
๐ฏ Application Overviewโ
:::info Tech Stack
| Repository | Commit | Last Updated |
|---|---|---|
| acesense-frontend | fff006e | 2026-05-28 (v1.0.4+1) |
- Framework: Flutter 3.10+ / Dart SDK ^3.10.3
- State: Provider
- Backend: Firebase (Auth, Firestore, Storage, Functions, Messaging)
- Video:
video_playermetadata extraction + Firebase Storage upload - Charts: fl_chart for analytics visualization
- UI: Material Design 3 with custom theming :::
๐๏ธ Application Architectureโ
๐ฑ Screen Mapโ
๐งญ Navigation Structureโ
๐ Project Structureโ
Regenerated from the tree on 2026-08-16. This section was previously wrong in a
way that mattered: it documented screens/, widgets/, providers/,
navigation/ and billing/ as top-level directories and an l10n/messages/*.json
catalogue. None of those exist. The app is organised by feature, state lives
in state/, routing is go_router, and localisation is ARB-based.
lib/
โโโ main.dart # Entry point + MaterialApp
โโโ firebase_options.dart # FlutterFire multi-platform config
โโโ config/ # AppConfig + build flags (kUseMockData, App Check)
โโโ data/ # mock.dart โ drill catalogue, sample notifications
โโโ dev/ # Developer-only preview harness (never shipped)
โโโ ds/ # Design system
โ โโโ theme.dart
โ โโโ tokens/ # DsColors, DsType, DsSpacing, DsRadius, DsMotion
โ โโโ widgets/ # DsButton, DsCard, DsBadge, DsAppShell, โฆ
โโโ features/ # One directory per feature, screens live here
โ โโโ analysis/ # match_analysis_screen, tabs/, mini_player, court_minimap
โ โโโ auth/ # welcome journey, sign in/up, onboarding, auth_layout
โ โโโ billing/ # billing_screen, upgrade_prompt
โ โโโ body/ # training-load + prevention
โ โโโ coach/ # player-facing coach dashboard
โ โโโ coach_mode/ # coach roster + student notes
โ โโโ flows/ # app_flows.dart โ upload/record/consent/share flows
โ โโโ help/ how_it_works/ legal/ notifications/ settings/ update/
โ โโโ home/ sessions/ new_session/ practice/ profile/
โ โโโ progression/ # badges, roadmap
โ โโโ wrapped/ # weekly recap
โโโ l10n/
โ โโโ app_localizations.dart # Re-export shim
โ โโโ arb/ # app_{en,de,es,fr,it,sv}.arb โ 1542 keys each
โ โโโ generated/ # flutter gen-l10n output, do not hand-edit
โโโ models/ # job, notification, tennis_analysis, user_stats
โโโ routing/ # app_router.dart (go_router) + NotFoundScreen
โโโ services/ # Firebase + domain services (see below)
โโโ state/ # Riverpod providers
โโโ locale_provider.dart
โโโ notifications_provider.dart
โโโ player_coach_provider.dart
โโโ progress_provider.dart
117 Dart files under lib/, 34 test files under test/.
Localisationโ
Six locales: en de es fr it sv, 1542 keys each, no drift. Source of truth
is lib/l10n/arb/app_*.arb; lib/l10n/generated/ is output โ never hand-edit
it. Regenerate with flutter gen-l10n.
Two rules the codebase now enforces:
tool/l10n_audit.pyruns in CI and fails the build on a hardcoded user-facing string. Excuse a string only in itsALLOW_EXACT/ALLOW_SITESlist, with the reason.- Data layers carry facts, not words.
PlayerCoachingService,DrillItemandShotTypeare reachable from cached providers, so any copy resolved there would survive a language switch. They expose ids and numbers; the widget layer resolves copy per render.ShotType.stableNameis the deliberate exception โ a locale-independent key for the one comparison that needs it.
๐ง Key Servicesโ
AuthServiceโ
VideoServiceโ
Other servicesโ
| Service | Purpose |
|---|---|
FirestoreService | Thin data layer over the users/, jobs/, feedback/ collections |
StorageService | Video preprocess + single-file upload pipeline + signed URL retrieval |
AiConsentService | First-upload AI analysis consent (App Store Guideline 5.1.1) |
๐ Localizationโ
| Language | Code | Status |
|---|---|---|
| ๐ฌ๐ง English | en | โ Complete |
| ๐ฉ๐ช German | de | โ Complete |
| ๐ซ๐ท French | fr | โ Complete |
| ๐ช๐ธ Spanish | es | โ Complete |
| ๐ฎ๐น Italian | it | โ Complete |
| ๐ธ๐ช Swedish | sv | โ Complete |
๐ State Managementโ
๐งช Testingโ
| Type | Tool | Coverage Target |
|---|---|---|
| Unit Tests | flutter_test | 80% |
| Widget Tests | flutter_test | 70% |
| Integration | integration_test | Critical paths |
| E2E | Patrol | Happy paths |
# Run all tests
flutter test
# Run with coverage
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
๐ Build & Deployโ
Developmentโ
# Run on device
flutter run
# Run on web
flutter run -d chrome
# Run on specific device
flutter run -d <device_id>
Productionโ
# iOS
flutter build ios --release
# Android
flutter build appbundle --release
# Web
flutter build web --release
firebase deploy --only hosting:app
โ๏ธ Cloud Functions Integrationโ
The app integrates with Firebase Cloud Functions for video processing.
Callable Functionsโ
| Function | Purpose |
|---|---|
requestUploadPath | Validate video metadata, create job doc, return upload path |
reportUploadFailure | Mark a still-pending job failed if the client upload fails before Storage finalization |
mergeChunkResults | Deferred chunk result merge support; not called by the current upload flow |
Example Usageโ
final functions = FirebaseFunctions.instanceFor(region: 'europe-west1');
// Request upload path
final callable = functions.httpsCallable('requestUploadPath');
final result = await callable.call({
'fileName': 'match.mp4',
'fileSize': 52428800,
'duration': 180,
'width': 1920,
'height': 1080,
'format': 'mp4',
});
final uploadPath = result.data['uploadPath'];
final jobId = result.data['jobId'];
try {
// Upload file bytes to uploadPath with Firebase Storage.
} catch (error) {
await functions.httpsCallable('reportUploadFailure').call({
'jobId': jobId,
'errorMessage': error.toString(),
});
rethrow;
}
Storage Triggerโ
The processVideoOnUpload function automatically triggers when videos are uploaded to the videos/{uid}/{sessionId}/ folder. If the upload fails before Storage finalization, the client calls reportUploadFailure so the job does not remain stuck in pending and the user's concurrent slot is released.
๐ฏ Next Stepsโ
- ๐จ Design System โ Ace components, design tokens, spacing, motion
- โก Key Features โ routing, analysis tabs, sample data, job status vocab
- ๐ง Frontend Setup Guide โ install Flutter, run the app
- ๐ฑ Frontend Dev Quickstart โ 5-min role-based tour
- ๐ API Reference โ callable functions
- ๐ Troubleshooting โ common Flutter/Firebase issues