This frontend is the operator-facing Flutter client for the Football Coach platform. It combines team administration, match management, player scouting views, asynchronous video-analysis workflows, real-time tactical alerts, and an embedded AI assistant inside one multi-platform application. The codebase currently contains roughly 115 Dart files under lib/, organized around feature modules, domain models, service clients, and a small design system.
From the code, the frontend is best understood as a thin but stateful orchestration layer over two HTTP APIs and one WebSocket channel:
- the classic backend API for team, match, player, settings, and assistant features
- the dedicated analysis API for video upload, status polling, file streaming, and segment timelines
- the tactical-alert WebSocket for live dashboard updates during or after analysis
The frontend sits at the top of the platform stack:
Flutter App
-> Backend API (:8000 /api) for CRUD, auth, RBAC, assistant, alerts metadata
-> Analysis API (:8001 /api) for upload, analysis history, files, segments, retry/cancel
-> WebSocket (:8000 /ws) for live tactical alerts
-> Local storage for JWT, theme, locale, assistant bubble visibility
Operationally, the app is designed for coaches, analysts, assistants, and player-level viewers. Role-sensitive navigation is enforced in the UI by AuthService, which decodes JWT claims and hides or disables privileged surfaces.
| Area | Implementation |
|---|---|
| Framework | Flutter with Dart >=3.8.0 <4.0.0 |
| State management | provider with ChangeNotifier services |
| Navigation | go_router with ShellRoute for authenticated shell screens |
| Networking | http through a shared ApiClient abstraction |
| Media | image_picker for uploads, video_player for previews and tactical dashboards |
| Persistence | shared_preferences for JWT, theme, locale, and assistant bubble state |
| Localization | flutter_localizations, intl, generated AR/EN/FR localizations |
| Data serialization | json_annotation and generated *.g.dart files |
| Visualization | fl_chart, custom painters, custom pitch/network widgets |
frontend/
├── lib/
│ ├── core/design_system/ # colors, spacing, theme, typography
│ ├── features/ # route-level product modules
│ ├── l10n/ # generated localizations + ARB files
│ ├── models/ # domain objects and JSON serializers
│ ├── routes/ # go_router configuration
│ ├── services/ # API clients, notifiers, realtime orchestration
│ └── widgets/ # shared widgets and navigation shell
├── assets/ # splash/art assets and launcher icon source
├── android/ ios/ linux/ web/ windows/
└── .env # runtime endpoint configuration
The application logic lives almost entirely under lib/; the platform directories are standard Flutter wrappers rather than custom business logic.
The startup path in lib/main.dart is a good summary of the application architecture:
- Flutter bindings are initialized.
.envis loaded throughflutter_dotenv.- The app selects base URLs differently for web and mobile:
- web prefers
localApiUrlandlocalAnalysisApiUrl - mobile prefers
BASE_URLandANALYSIS_BASE_URL
- web prefers
- Two
ApiClientinstances are created:- one for the classic backend
- one for the dedicated analysis backend
AuthServicerestores the JWT fromSharedPreferences, injects the token into both API clients, and fetches/users/me.- Theme, locale, and assistant-bubble preferences are restored.
- Providers are registered with
MultiProvider. MaterialApp.routeris launched with a timed splash overlay and dynamic router redirection.
This makes the frontend a composed runtime rather than a passive widget tree: transport, auth, settings, and feature-level notifiers are all assembled before the first authenticated screen renders.
lib/core/design_system/ defines the visual primitives:
AppColors: emerald primary palette, crimson secondary palette, light and dark neutral surfacesAppSpacing: shared spacing and border-radius tokensAppTypography: shared text stylesAppTheme: Material 3 light and dark themes built from the tokens above
The theme layer is intentionally lightweight. Most product differentiation happens in feature widgets rather than through a large token system.
lib/routes/app_router.dart uses GoRouter with two routing zones:
- unauthenticated routes:
/login/register
- authenticated shell routes:
/strategie/matches/players/analyze/settings
The shell is implemented by ScaffoldWithNavBar, which contributes:
- a bottom navigation bar
- a persistent floating AI assistant bubble overlay
- role-aware nav visibility
The Analyze tab is hidden for users whose decoded role resolves to player.
The service layer is the real application backbone.
Core cross-cutting services:
ApiClient: shared HTTP abstraction, auth-header injection, query-parameter handling, API exception normalizationAuthService: JWT persistence,/tokenlogin,/register,/users/me, RBAC claim parsing, logoutThemeNotifier: theme persistence and togglingLocaleNotifier: locale persistenceChatBubbleNotifier: visibility and overlay-open state for the embedded assistant
Domain CRUD services:
TeamServiceMatchServicePlayerServiceStaffServiceEventServiceFormationServiceMatchLineupServicePlayerMatchStatisticsServiceNoteServiceReunionServiceTrainingSessionService
Analysis and realtime services:
VideoAnalysisService: upload, progress polling, SSE segment subscription, cancel, retryAnalysisService: analysis history, file URLs, JSON preview loading, segment listingTacticalAlertService: WebSocket connection, alert history refresh, decision feedback, decision metricsAssistantService:/assistant/queryintegration
The models mirror backend payloads and are intentionally explicit. Representative entities include:
- identity and organization:
User,Staff,Team - competition:
Match,Event,MatchDetails,MatchLineup,MatchEvent - player analysis:
Player,PlayerMatchStatistics,MatchTeamStatistics - asynchronous analysis:
AnalysisReport,AnalysisSegment,TacticalAlert - collaboration:
MatchNote,ChatMessage,Reunion,TrainingSession
Many models use generated serializers, which makes the frontend strongly coupled to backend payload shape but simpler to reason about during debugging.
| Feature | Primary screens | Backing services | Purpose |
|---|---|---|---|
| Auth | login_screen.dart, register_screen.dart |
AuthService |
Entry point and token bootstrap |
| Strategie | strategie_screen.dart, training and reunion screens |
TrainingSessionService, ReunionService |
Planning and team operations |
| Matches | matches_screen.dart, add-match/event, match details |
MatchService, EventService, MatchLineupService, FormationService |
Schedule, filter, inspect, and enrich matches |
| Players | players_screen.dart, add-player, profile/details/statistics |
PlayerService, PlayerMatchStatisticsService |
Roster management and player analytics |
| Analyze | analyze_screen.dart, new-analysis, history, preview |
VideoAnalysisService, AnalysisService, NoteService |
Upload video, watch progress, inspect segment outputs |
| Match Statistics | match_statistics_screen.dart, tactical dashboard widgets |
AnalysisService, TacticalAlertService |
Post-analysis visual analytics and realtime tactical review |
| Assistant | chat_assistant_page.dart, floating bubble |
AssistantService, ChatBubbleNotifier |
Conversational tactical Q&A |
| Settings | settings sub-screens, staff list, preferences, about | AuthService, ChatBubbleNotifier, domain services |
Admin tasks and local preferences |
The auth flow is straightforward but important:
- The user submits credentials to
/token. - The backend returns an RS256-signed JWT.
AuthServicestores the token and parses claims such as:user_typestaff_idteam_idpermission_levelapp_roleapp_permissions
- The router refreshes, redirects away from auth pages, and enables the shell.
- Shell navigation and screen actions check permissions using helper getters like
canManagePlayers,canManageReunions, andhasPermission.
The analysis flow is the most sophisticated frontend sequence in the repository:
- The user selects a video with
image_picker. VideoAnalysisService.uploadAndAnalyzeVideo()streams the multipart upload to the analysis backend.- Upload progress is shown in real time.
- After the backend responds with an analysis id:
- the service starts SSE consumption from
/analysis/{analysis_id}/segments/stream - the service starts polling
/analysis_status/{analysis_id}
- the service starts SSE consumption from
- New segment cards are inserted into the UI as they arrive.
- Completed assets are later browsed through the history screen and preview pages.
- File playback uses authenticated URLs generated by
AnalysisService.
This is a hybrid streaming architecture: upload and status are request-response, segments are SSE, and tactical events are handled separately through WebSockets.
The tactical dashboard on the match-statistics side combines three different data sources:
- REST fetch of latest analysis metadata for the selected match
- Authenticated video streaming for tracking previews
- WebSocket subscription to
/ws/alerts/{match_id}viaTacticalAlertService
The dashboard also supports manual video-to-match synchronization by writing a "video anchor" through MatchService. Once anchored, clicking a tactical alert seeks the analysis video to the inferred timestamp.
The AI assistant can be opened in two ways:
- as a dedicated page
- as a floating overlay available from shell pages
Messages are posted to /assistant/query, and typing/loading state is handled locally inside the chat screen. The assistant feature is deliberately lightweight on the client; almost all reasoning lives in the backend RAG stack.
| Interface | Consumer | Purpose |
|---|---|---|
GET /api/analysis/{id}/segments/stream |
VideoAnalysisService |
Live segment timeline during analysis |
GET /api/analysis_status/{id} |
VideoAnalysisService |
Polling progress and live stats |
GET /ws/alerts/{match_id} |
TacticalAlertService |
Live tactical alert packets |
POST /api/decision/feedback or /decision/feedback |
TacticalAlertService |
Coach feedback on recommendations |
GET /api/decision/metrics or /decision/metrics |
TacticalAlertService |
Decision-effectiveness dashboards |
The frontend expects a .env file. The code reads the following keys:
BASE_URL=http://localhost:8000/api
ANALYSIS_BASE_URL=http://localhost:8001/api
localApiUrl=http://localhost:8000/api
localAnalysisApiUrl=http://localhost:8001/apiNotes:
- web builds prefer
localApiUrlandlocalAnalysisApiUrl - non-web builds use
BASE_URLandANALYSIS_BASE_URL - the auth token is reused across both backend surfaces
- Flutter SDK compatible with Dart 3.8
- A running classic backend on port
8000 - A running analysis backend on port
8001 - Optional: the tracking engine running behind the analysis backend for end-to-end analysis
cd frontend
flutter pub getIf model classes change and generated serializers must be refreshed:
dart run build_runner build --delete-conflicting-outputscd frontend
flutter run -d chromeFor Android or desktop targets, use the standard Flutter device selectors.
- Clean separation between transport concerns and feature widgets
- Explicit domain models that largely mirror backend contracts
- Role-aware UI behavior without over-complicating routing
- Good use of multiple transport modes: HTTP, SSE, and WebSocket
- Internationalization is built in rather than retrofitted
- The analysis workflow is product-rich: upload progress, segment streaming, preview playback, history, retry, and tactical overlays all coexist coherently
- Several large screens, especially analysis and dashboard screens, combine presentation and orchestration state in one widget, which raises maintenance cost.
- The app has limited visible automated test coverage in the frontend repository itself.
- The design system is intentionally small; visual consistency depends heavily on each feature module using shared widgets correctly.
- The analysis UI assumes backend endpoint compatibility very closely, so contract drift on the backend will surface quickly in the client.
- There is no checked-in frontend
.env.example, even though endpoint configuration is mandatory.
For a new contributor, the fastest high-signal walkthrough is:
lib/main.dartlib/routes/app_router.dartlib/services/api_client.dartlib/services/auth_service.dartlib/services/video_analysis_service.dartlib/services/tactical_alert_service.dartlib/features/analyze/...lib/features/match_statistics/presentation/tactical_dashboard_page.dart
That sequence reproduces the actual runtime stack from bootstrap to the most distinctive product flows.