ADR-008-F: Flutter Mobile (One App, Four Roles)¶
Status: Accepted Date: 2026-04-16 Decision Makers: Gautham Chellappa
Context¶
The existing ASG mobile strategy consists of four separate apps (aCMS employee, aAMS asset, aTask task, aTeam team). Four App Store listings, four codebases, four sets of App Store reviews, four installation steps per worker. Field workers who wear multiple hats carry four icons.
Finnest replaces them with one app. The question is which framework, and whether to adapt the existing Flutter v5.0.4 codebase or rewrite.
Decision¶
One Flutter mobile app (separate repo: finnest-mobile) with role-based feature visibility. Adapt the existing Flutter v5.0.4 codebase rather than rewrite from scratch.
One app, four (five) roles¶
| Role | Screens | Primary interaction |
|---|---|---|
| Field Worker | ~5 (home/agent, roster, timecard, leave, notifications) | Agent chat, NFC clock |
| Supervisor | ~10 (above + team view, approvals, shifts, tasks, incident) | Notifications + agent chat |
| Asset Manager | ~10 (above + equipment, maintenance, fleet) | Quick actions + agent chat |
| Client Contact | ~5 (assigned workers, roster, placements, billing) | Client portal |
Role is derived from the JWT claim returned by the server — same user can have different roles in different orgs.
Framework: Flutter¶
- Existing codebase proves NFC, GPS, push, biometric, offline capable
- Bounded polyglot cost (~5-10K LOC of Dart vs 150K+ LOC of Elixir backend)
- Single codebase → iOS + Android builds
- Mature native capabilities for every field-work requirement
Architecture summary¶
- BLoC state management (existing, CQM-08)
- GoRouter navigation with role-based guards
- SQLite offline event queue (drift or sqflite) mirroring server event shape
- Phoenix Channels over WSS for real-time (notifications + agent chat streaming)
- REST
/api/v1/*for CRUD and sync - JWT + biometric-unlocked refresh token (SE-18)
- FCM push with encrypted payload (no PII in notification body)
Full detail in ../architecture/mobile.md.
Alternatives Considered¶
| Framework | Rejected because |
|---|---|
| LiveView Native | NFC, offline SQLite, background location not production-ready; experimental |
| React Native | Ecosystem churn risk; bridge architecture brittle; fourth language in the stack (JS) |
| KMP + Compose | No code sharing with Elixir backend; iOS Compose still maturing; duplicate UI code |
| PWA | Can't do NFC; weak enterprise MDM; no App Store presence for defence Intune |
| Rewrite Flutter from scratch | Throws away proven NFC / GPS / push implementations; 8-10 weeks to same capability |
| Keep four separate apps | Four apps to maintain, four store listings, four reviews per release, installation friction |
Consequences¶
Positive:
- 4× reduction in mobile codebase maintenance surface
- One App Store listing — one review per release
- Role-based visibility: field workers not overwhelmed; asset managers have what they need
- Existing Flutter proves production viability for NFC / GPS / push / offline
- Bounded polyglot cost — Dart is isolated to the mobile tier
- Clean REST + Channels API layer keeps mobile client swappable (LiveView Native re-evaluation trigger at 2027)
Negative:
- Dart is a second language in the stack alongside Elixir
- Separate repo means separate CI / release / test tooling
- App Store submission process is separate from backend deployment
- Role-based UI adds complexity over single-role app — feature flags per role
Migration effort (B07):
| Work item | Duration |
|---|---|
| API migration (v2 CodeIgniter → Finnest Phoenix) | 2 weeks |
| Offline sync engine | 2 weeks |
| Agent chat interface | 1 week |
| Role-based navigation | 1 week |
| Asset management screens | 1 week |
| UI refresh (Finnest design system) | 1 week |
| Testing + App Store submission | 1 week |
| Total | ~8-10 weeks (Phase 2, Weeks 13-20) |
IRAP flavor (IR-15, IR-16):
- Separate Flutter flavor (
flutter build apk --flavor irap) produces distinct APK/IPA - Microsoft Intune MDM distribution only — not public App Store
- Firebase Analytics disabled; FCM kept with encrypted payload only
- Mandatory biometric + PIN; 5-min idle lock
Tipping points for re-evaluation:
- LiveView Native matures for NFC / offline / background location (2027 review per main architecture OI-08)
- Flutter 4.x introduces breaking changes that cost more than a rewrite would — unlikely
- Team grows such that a native iOS + Android split team is affordable — unlikely at current scale
Relationship to Guardrails¶
Enforces / is enforced by: CQM-01 through CQM-12 (12 Flutter mobile guardrails), SE-17, SE-18 (mobile security), IR-15, IR-16 (IRAP mobile).