Treeper — Mobile
Flutter app (iOS + Android). Implements the user-facing side of every
spec under ../../specs/features, starting with
0001-trip-planner.
For the why and the architecture, see ADRs: 0001 Flutter, 0002 Supabase + NestJS API.
# from the monorepo root, oncefvm install stablefvm use stable
# this appcd apps/mobilefvm flutter pub getfvm flutter run # connected device or simulatorfvm flutter test # unit + widget testsThe Flutter SDK is pinned via .fvmrc at the repo root. Always invoke
Flutter through fvm flutter <cmd> so the right SDK is used.
Layout (planned)
Section titled “Layout (planned)”apps/mobile/├── lib/│ ├── main.dart app entrypoint + router│ ├── core/ config, env, supabase client, http│ ├── features/│ │ └── trips/ feature 0001: trip planner + tracker│ │ ├── data/ repos, sync queue, local store│ │ ├── domain/ entities, value objects, use cases│ │ └── presentation/ pages, widgets, controllers│ └── shared/ widgets and utilities cutting across features└── test/ ├── unit/ └── widget/State management, routing, and DI choices will land as ADRs once we have real evidence to decide.
Environment
Section titled “Environment”Local secrets live in .env files outside the repo. The Env class
(to be added under lib/core/env/) will read either:
--dart-define-from-file=env/local.jsonfor local dev, or- platform-supplied env vars in CI.
Required keys (will be enumerated in a follow-up ADR):
SUPABASE_URLSUPABASE_ANON_KEYTREEPER_API_BASE_URLSENTRY_DSN_MOBILE (optional)Never commit secrets.
Testing
Section titled “Testing”- Unit tests for pure Dart logic in
test/unit/. - Widget tests for UI components in
test/widget/. - Integration tests live under
integration_test/once we have flows worth end-to-end testing.
Acceptance criteria from feature specs map 1:1 to test names where
possible. Example: a test exercising AC-5 of feature 0001 is named
feature_0001/AC-5_drag_activity_across_days_test.dart.
Push notifications (spec 0011, Slice 2)
Section titled “Push notifications (spec 0011, Slice 2)”The app uses Firebase Cloud Messaging for push (Android + iOS via APNs).
Code wiring is complete — bootstrap.dart calls Firebase.initializeApp()
and FcmService (lib/features/notifications/data/fcm_service.dart)
registers tokens with the backend, refreshes the feed on foreground
pushes, and deep-links tapped pushes to the import-review screen.
What still has to be set up out-of-band (one-time):
- Firebase project — create one in the Firebase console.
- Android — drop
android/app/google-services.jsonfrom that project. Make suregoogle-servicesGradle plugin is applied inandroid/build.gradle+android/app/build.gradle. - iOS — drop
ios/Runner/GoogleService-Info.plist. In Xcode, enable thePush Notificationscapability and theBackground Modes → Remote notificationscapability on the Runner target. Upload an APNs auth key (or sandbox cert) to the Firebase console. - Backend — set
FCM_SERVICE_ACCOUNT_B64on the API service to a base64-encoded service-account JSON with thecloudmessaging.messages.createpermission. Without this var thePushWorkerdrains rows but skips actual sends.
The app boots fine without these files — Firebase.initializeApp()
fails silently and FcmService.start() short-circuits. The in-app
Notifications tab and Realtime banner continue to work.
Build & release
Section titled “Build & release”iOS and Android release builds go to TestFlight and Play Internal
respectively. The release pipeline lives in infra/ and is not yet
automated; v0 will likely use manual Fastlane lanes invoked locally.