§1Overview
Rosie AI is a Flutter app for keeping personal memories — journaling, photo albums, an AI chat ("Rosie"), and people connections.
Package com.bandraroad.heyrosie on Android, com.bandraroad.rosieai on iOS.
Stack
- Flutter 3.x (Dart
^3.5.3) - State:
provider+ ChangeNotifier - Theme: MobX
- DI: GetIt
- Routing: Navigator 1.0 named routes
- Local cache:
sqflite+SharedPreferences
Backend
- Firebase project
rosie-main - Firestore + Firebase Auth + Storage + Remote Config + Analytics
- REST APIs (env-switchable: staging / production)
- OneSignal push
- Socket.io for real-time
Architecture at a glance
JournalPage, Dashboard,
NewMemoryBuilder, ChatScreen, …"] SharedWidgets["Shared Widgets
lib/widgets/, lib/core/widgets/"] end subgraph State["State Layer"] Providers["20 Global ChangeNotifierProviders
+ feature-local providers"] Theme["MobX ThemeStore"] end subgraph ServiceLayer["Service Layer · lib/core/services/"] Services["28 services
analytics, firebase, notification,
journal_api, vault, …"] DI["GetIt locator"] end subgraph Persistence["Persistence"] Firestore[("Firestore
rosie-main")] Storage[("Firebase
Storage")] SQLite[("SQLite
sqflite")] Prefs[("Shared
Preferences")] end subgraph External["External"] REST["REST APIs
(env-switchable)"] OneSignal["OneSignal
Push"] Socket["Socket.io
Real-time"] OAuth["Google · Apple ·
Facebook · Spotify · …"] end Screens --> Providers Screens --> SharedWidgets Screens --> Theme Providers --> Services Services -.uses.-> DI Services --> Firestore Services --> Storage Services --> SQLite Services --> Prefs Services --> REST Services --> OneSignal Services --> Socket Services --> OAuth classDef ui fill:#ff6b5e,stroke:#ff6b5e,color:#fff classDef state fill:#7cb7ff,stroke:#7cb7ff,color:#0f1115 classDef svc fill:#ffb86c,stroke:#ffb86c,color:#0f1115 classDef ext fill:#bd93f9,stroke:#bd93f9,color:#0f1115 class Screens,SharedWidgets ui class Providers,Theme state class Services,DI svc class REST,OneSignal,Socket,OAuth ext
§2Screenshot → APK workflow
The intended headline workflow. Drop a screenshot + describe the bug, get back a working APK.
AppCode/CLAUDE.md), and the loop has been run end-to-end once on a clean APK build. It is not yet the day-to-day flow for this project. Treat this section as the target operating model; expect rollout to be incremental.
The contract
| You give | You get |
|---|---|
A screenshot (paste in chat or drop in Frontend-Plan/inbox/) |
A debug APK at Frontend-Plan/builds/app-debug-<timestamp>.apk |
| One sentence describing what's wrong | A 3–5 line report: bug, file:line, APK path, install command |
The 7 steps
- Read the screenshot — identify the screen, match it to a Dart file under
AppCode/lib/features/ - Locate the code — open the widget + its provider + relevant constants
- State the bug in one sentence — if you can't, you don't understand it yet
- Fix — minimal change, matching existing patterns. No drive-by refactors.
- Verify it compiles:
flutter analyzemust pass - Build the APK:
flutter build apk --debug, copy to timestamped slot - Report back in 3–5 lines
+ 1-sentence bug"/] --> Read["1 · Read image
(identify screen)"] Read --> Locate["2 · Locate code
lib/features/<screen>.dart"] Locate --> Diagnose["3 · State bug
in one sentence"] Diagnose --> Fix["4 · Fix
(minimal Edit)"] Fix --> Analyze{"5 · flutter analyze"} Analyze -->|fail| Fix Analyze -->|pass| Build["6 · flutter build apk --debug"] Build --> Copy["cp to
builds/app-debug-<ts>.apk"] Copy --> Report[/"7 · 3–5 line report
📦 APK ready"/] classDef input fill:#bd93f9,stroke:#bd93f9,color:#0f1115 classDef step fill:#7cb7ff,stroke:#7cb7ff,color:#0f1115 classDef gate fill:#ffb86c,stroke:#ffb86c,color:#0f1115 classDef output fill:#50fa7b,stroke:#50fa7b,color:#0f1115 class Screenshot input class Read,Locate,Diagnose,Fix,Build,Copy step class Analyze gate class Report output
Trigger phrases
Any of these enters the workflow: "fix this", "ship a build", "fix and build", "do this", or any image with a problem description.
Install on the emulator
# JDK 17 required — Gradle 8.9 rejects JDK 26
export JAVA_HOME=$(/usr/libexec/java_home -v 17)
adb -s emulator-5554 install -r \
Frontend-Plan/builds/app-debug-<timestamp>.apk
# Launch:
adb -s emulator-5554 shell am start -n com.bandraroad.heyrosie/.MainActivity
adb install fails with INSTALL_FAILED_UPDATE_INCOMPATIBLE, uninstall first:
adb -s emulator-5554 uninstall com.bandraroad.heyrosie
§3Build & run
# All commands run from AppCode/
cd /Users/gauravtewari/PycharmProjects/AutomatedTestingApp/AppCode
# JDK 17 — REQUIRED. System default JDK 26 will fail.
export JAVA_HOME=$(/usr/libexec/java_home -v 17)
export PATH=$JAVA_HOME/bin:$PATH
flutter pub get
flutter analyze
flutter test # unit + widget
flutter test integration_test/ # on a device/emulator
# Run on the project's emulator (rosie_proper / emulator-5554):
flutter run -d emulator-5554
# Build debug APK:
flutter build apk --debug
# Output: build/app/outputs/flutter-apk/app-debug.apk
# MobX codegen (after editing observables):
dart run build_runner build --delete-conflicting-outputs
rosie_proper · 1080×2340 · serial emulator-5554.Start:
~/Library/Android/sdk/emulator/emulator -avd rosie_proper -no-window -no-audio &
§4Entry & app shell
Startup sequence (lib/main.dart)
WidgetsFlutterBinding.ensureInitialized()IntentHandler.handleIntent()— incoming Android intents / deep linkssetupLocator()— GetIt DI container (lib/core/di/locator.dart)Firebase.initializeApp()with platform options (rosie-mainproject)- Firestore offline persistence + unlimited cache (Samsung-device quirk noted in code)
initializeApp()— NotificationService, RemoteConfigService, PhotoCacheService, AnalyticsService- OneSignal init (lines 138–303 in
main.dart) with deep-link router
Root widget
MyApp (line 325 in main.dart) wraps everything with:
MultiProvider— 20 ChangeNotifierProviders (see state management)FeatureDiscovery+ShowCaseWidgetfor tutorialsMaterialAppwithnavigatorKey, themes,onGenerateRoute, analytics observer
Theme
Driven by MobX themeStore (lib/features/settings/theme_store.dart), observed in MyApp.build. Light/dark/system modes.
Startup sequence
NotificationService
UserProfileService
BiometricAuthService Main->>FB: initializeApp(rosie-main) Main->>FB: Firestore.settings (offline, ∞ cache) Main->>Svc: initializeApp() Note over Svc: NotificationService
RemoteConfigService
PhotoCacheService
AnalyticsService Main->>OS1: OneSignal.initialize(appId) OS1-->>Main: deep-link router ready Main->>App: runApp(MyApp) App->>App: MultiProvider (20 providers) App->>App: MaterialApp(home: SplashScreen) App-->>OS: first frame rendered
§5State management
provider + ChangeNotifier for app state, MobX for theme only, GetIt for service singletons. No Riverpod, no Bloc.
Global providers
| Provider | File | Responsibility |
|---|---|---|
AuthenticationProvider | lib/providers/authentication_provider.dart | Firebase Auth (email, Google, Apple, Facebook) |
HomepageProvider | lib/features/home/provider/... | Dashboard data |
HomepageStateProvider | lib/features/home/provider/... | Home UI state (tabs, filters) |
CollectionMemoryProvider | lib/features/home/provider/... | Memory collections |
TimelineProvider / TimelineStateProvider | lib/features/home/provider/... | Timeline data & UI state |
MemoryViewerProvider | lib/providers/memory_viewer_provider.dart | Memory detail & galleries |
GlobalNotificationProvider | lib/providers/global_notification_provider.dart | In-app toasts |
ThirdPartyServicesProvider | lib/providers/third_party_services_provider.dart | Google Photos, Calendar, Spotify, … |
JournalProvider large | lib/features/journal/provider/... | Journal CRUD + AI summaries — 500+ lines, tech-debt candidate |
JournalCartProvider | lib/features/journal/provider/... | Unsaved journal drafts |
MemoryBuilderProvider mixed | lib/providers/memory_builder_provider.dart | Memory creation + Firestore user data + OneSignal token |
VoiceRecorderService | lib/providers/voice_recorder.dart | Voice input (doubles as ChangeNotifier) |
ChatProvider | lib/features/chat_screens/chat_provider.dart | Chat with Rosie AI |
ProfileDataProvider | lib/features/drawer/provider/... | User profile state |
ConnectionProvider / FamProvider | lib/features/Connections/provider/... | People & family |
NotesProvider / NoteCartProvider | lib/features/Connections/provider/... | Connection notes |
NewMemoryProvider | lib/features/memory_builder/... | New memory builder logic |
PhotoUploadProvider | lib/providers/photo_upload_provider.dart | Upload progress |
Consumption patterns
// Read once (no rebuild):
context.read<JournalProvider>().fetchEntries();
// Listen & rebuild on change:
final entries = context.watch<JournalProvider>().entries;
// Outside a widget (e.g. from a service callback):
Provider.of<JournalProvider>(
navigatorKey.currentContext!, listen: false,
);
DI (GetIt)
// lib/core/di/locator.dart
locator.registerSingleton<FirebaseService>(FirebaseService());
locator.registerSingleton<NotificationService>(NotificationService());
locator.registerLazySingleton(() => UserProfileService());
locator.registerLazySingleton(() => BiometricAuthService());
State flow: a typical user action
Example: user taps "save" on a journal entry.
(Widget) participant Provider as JournalProvider
(ChangeNotifier) participant API as JournalApi
(Service) participant FB as Firestore participant Cache as SQLite cache User->>Widget: tap "Save" Widget->>Provider: context.read<JournalProvider>()
.saveEntry(entry) Provider->>Provider: _isLoading = true Provider->>Provider: notifyListeners() Widget-->>User: spinner shown Provider->>API: POST /journal/entries API->>FB: write doc FB-->>API: ack API-->>Provider: JournalEntry Provider->>Cache: upsert entry Provider->>Provider: _entries.add(entry)
_isLoading = false Provider->>Provider: notifyListeners() Widget->>Widget: context.watch rebuilds Widget-->>User: entry appears in list
notifyListeners().§6Routing & navigation
Navigator 1.0 with named routes via onGenerateRoute. Router lives at lib/core/navigation/app_router.dart. Not GoRouter.
| Route | Screen | Notes |
|---|---|---|
/ | SplashScreen | Auth-state gate; initial routing |
/home | Dashboard | 5-tab hub: Home, Journal, Timeline, Connections, Chat |
/memories/{id} | NewMemoryViewer(batchId) | Dynamic param; deep-linked |
/login | AuthScreen | Email + OAuth |
/onboarding | OnboardingScreen | First-run flow |
/settings | SettingsScreen | Preferences |
/profile-home | ProfileHomeScreen | User profile |
/tutorials | TutorialScreen | Feature tutorials |
/faq | FAQScreen | Backend-driven FAQs |
/help | HelpScreen | Support resources |
/get-in-touch | GetInTouchScreen | Contact form |
/third-party | ThirdPartyScreen | Integrations list |
/connect-services | ConnectServicesScreen | OAuth callback |
/memory-ideas | MemoryIdeasScreen | AI suggestions |
Deep linking
OneSignal payload keys memoryId, deepLink, route are translated to navigator pushes in main.dart:216–290.
Programmatic navigation
final navigatorKey = GlobalKey<NavigatorState>();
navigatorKey.currentState?.pushNamed('/home');
Navigation map
SplashScreen"] Splash -->|signed in| Dashboard Splash -->|new user| Onboarding["/onboarding"] Splash -->|signed out| Login["/login
AuthScreen"] Login -->|success| Dashboard Onboarding --> Dashboard Dashboard["/home · Dashboard"] --> TabHome["Tab: Home"] Dashboard --> TabJournal["Tab: Journal"] Dashboard --> TabTimeline["Tab: Timeline"] Dashboard --> TabConnections["Tab: Connections"] Dashboard --> TabChat["Tab: Chat"] TabHome --> Memory["/memories/{id}
NewMemoryViewer"] TabHome --> Ideas["/memory-ideas"] TabJournal --> MemoryBuilder["NewMemoryBuilder"] TabConnections --> ProfileHome["/profile-home"] Dashboard -.drawer.-> Settings["/settings"] Dashboard -.drawer.-> FAQ["/faq"] Dashboard -.drawer.-> Help["/help"] Dashboard -.drawer.-> Tutorials["/tutorials"] Dashboard -.drawer.-> ThirdParty["/third-party"] ThirdParty --> ConnectSvc["/connect-services
(OAuth callback)"] PushDL{{"Push notification
OneSignal deep link"}} PushDL -.memoryId.-> Memory PushDL -.route.-> Dashboard classDef entry fill:#ff6b5e,stroke:#ff6b5e,color:#fff classDef tab fill:#7cb7ff,stroke:#7cb7ff,color:#0f1115 classDef drawer fill:#ffb86c,stroke:#ffb86c,color:#0f1115 classDef push fill:#bd93f9,stroke:#bd93f9,color:#0f1115 class Splash,Login,Onboarding entry class TabHome,TabJournal,TabTimeline,TabConnections,TabChat tab class Settings,FAQ,Help,Tutorials,ThirdParty,ConnectSvc drawer class PushDL push
lib/core/navigation/app_router.dart. Dashboard owns 5 tabs; push notifications can deep-link anywhere.§7Feature modules
Each feature in lib/features/ follows the same shape: screens/, provider/, models/, services/, widgets/.
| Feature | Purpose | Entry class |
|---|---|---|
splash | Auth-state gate | SplashScreen |
auth | Email + OAuth login | AuthScreen |
auth_dialogs | Verification & reset dialogs | — |
onboarding | First-run welcome | OnboardingScreen |
home | Dashboard with 5 tabs | Dashboard |
journal | Daily journaling, moods, AI summaries | JournalPage |
memory | Photo picker (incl. Google Photos) | SelectPhotosScreen |
memory_builder | Collage editor, narration, sharing | NewMemoryBuilder / NewMemoryViewer |
memory_ideas | AI memory prompts | MemoryIdeasScreen |
Connections | People & family management | Connections |
chat_screens | Chat with Rosie AI | ChatScreen |
drawer | Side menu, FAQ/Help/Tutorials | — |
biographer stub | Life-story capture (placeholder) | BiographerComingSoonScreen |
settings | Theme, biometric, account | SettingsScreen |
profilespage | Profile view/edit | ProfileHomeScreen |
share_rosie | Shareable collages & wraps | CreateTypeScreen → … → ExportScreen |
third_party | Google Calendar, Spotify, etc. | ThirdPartyScreen |
introduction | Post-login welcome flows | IntroductionScreen |
apps | App-specific integrations | features/apps/whatsapp/ |
Example: journal feature layout
features/journal/
├── journal_page.dart // main screen
├── models/
│ ├── journal_model.dart
│ ├── journal_content_model.dart
│ └── focus_item_model.dart
├── provider/
│ ├── journal_provider.dart // CRUD, AI summaries
│ └── journal_cart_provider.dart // drafts
├── services/
│ ├── journal_api.dart
│ ├── journal_data_service.dart
│ └── journal_firebase_service.dart
└── widgets/ // notes, prompts, voice input
Feature module shape
Every feature in lib/features/<name>/ follows this internal structure.
main_page.dart"] Feature --> Provider["provider/
main_provider.dart
cart_provider.dart"] Feature --> Models["models/
data classes
(serialisable)"] Feature --> Services["services/
api.dart
firebase_service.dart"] Feature --> Widgets["widgets/
feature-local UI"] Screens -->|read/watch| Provider Screens -->|use| Widgets Provider -->|calls| Services Provider -->|holds| Models Services -->|returns| Models Services -->|persists| External[("Firestore
REST
SQLite")] classDef root fill:#ff6b5e,stroke:#ff6b5e,color:#fff classDef child fill:#7cb7ff,stroke:#7cb7ff,color:#0f1115 class Feature root class Screens,Provider,Models,Services,Widgets child
screens/ + widgets/, state in provider/, I/O in services/.§8Core services
lib/core/services/ holds the cross-cutting singletons and API wrappers used app-wide.
Services index
| Service | Purpose |
|---|---|
firebase_service | Firebase init wrapper; guards duplicate apps |
notification_service | Local notifications; creates channels |
analytics_service | Firebase Analytics; consent-gated; route observer |
remote_config_service | Firebase Remote Config — feature flags & env |
photo_cache_service | Image caching & cleanup on foreground |
deep_link_service | App-link handling post-first-frame |
user_profile_service | Cached user profile; SharedPreferences backed |
biometric_auth_service | Fingerprint / Face unlock |
notification_trigger_service | Scheduled local notifications |
memory_note_count_service | Note counts per memory |
connection_service | REST for connections |
profile_api_service | Profile CRUD against backend |
journal_api | Journal REST endpoints |
journal_firebase_service | Journal entries in Firestore |
notes_api | Notes CRUD |
fam_api_service | Family relationships |
user_connections_service | Firestore connection docs |
collaborator_service | Multi-user memory access |
face_image_service | Face detection & cropping |
link_metadata_service | OpenGraph extraction |
location_service | GPS + geocoding |
exif_service | Photo EXIF parsing |
screen_time_service | Time-on-screen analytics |
vault_upload_service | Photo uploads to Firebase Storage |
photo_vault_sync_service | Bi-directional vault sync |
intent_handler | Android intent processing |
update_service | App version check |
premission_service typo | Permission wrapper — note misspelling, do not rename in isolation |
Local database
cache_manager_sqflite.dart in lib/core/database/ — SQLite via sqflite v2.4.1. Backs journal entries, notes, offline sync.
Environment switching
// lib/core/config/api_urls.dart
enum ApiEnvironment { production, staging }
class ApiUrls {
static ApiEnvironment _currentEnvironment = ApiEnvironment.staging;
static setEnvironment(ApiEnvironment env) =>
_currentEnvironment = env;
}
No rebuild needed to flip — call ApiUrls.setEnvironment(...) at runtime.
§9Shared widgets
Cross-cutting UI lives in lib/widgets/ and lib/core/widgets/.
| Widget | Use |
|---|---|
CustomAppBar | Standard header with back, title, actions |
CustomShareDialog | Share memory dialog |
FullScreenImageViewer | Fullscreen photo gallery with swipe |
PermissionDialog | System-permission request |
LoadingSpinner | Animated loading indicator |
MemorySkeletonLoader | Shimmer placeholder for memory cards |
SyncCompleteDialog | Confirmation when sync finishes |
LinkPreviewCard | OpenGraph card (image, title, description) |
DashedBorderPainter | Custom dashed border |
DeleteNoteDialog | Note deletion confirm |
ExitDialog | App-exit confirm |
SnackbarUtils | Toast helper |
WebViewDocumentScreen | Embedded document viewer |
WeeklyWrapScreen / MonthlyWrapScreen / YearlyWrapScreen | Period summaries |
WelcomeGuideScreen | Feature tutorial guide |
NotificationDrawer | Notification center overlay |
RippleEffect | Touch feedback |
ScreenSize | Responsive layout helper |
§10Backend integration
Firebase (rosie-main)
- Auth — email, Google, Apple, Facebook, phone
- Firestore —
users/{uid},memories/{id},journals/{uid}/entries/{id},connections/{uid}/connections/{id},notes/{id} - Storage — photos & videos
- Analytics — consent-gated; tracks
appOpened,appForegrounded,appBackgrounded, route changes - Remote Config — feature flags / env
- App Check enabled
REST APIs
Endpoints centralised in lib/core/config/api_urls.dart. Categories: chat, journal, memory, connection, profile. Auth via Firebase ID tokens in headers. http package v1.3.0. SQLite fallback for offline.
OneSignal push
- App ID
0adc76ab-1f0f-43f7-9b3f-1121eb197997— hardcoded inmain.dart:139(see backlog) - Player ID stored on the Firestore user doc
- Deep-link payload keys:
memoryId,deepLink,route
Real-time
socket_io_client v3.0.0 — used for real-time notifications & chat.
Third-party integrations
Google Photos · Google Calendar (+ Calendly) · Spotify · WhatsApp · Facebook · Apple Sign-In.
§11Build & tooling
Android
- Namespace:
com.bandraroad.heyrosie - minSdk 24, targetSdk 36
android/app/build.gradleversion: 1.0.23 / 446 — drifts frompubspec.yaml1.0.16+439. Reconcile before release.- Signing via
android/key.properties(not in repo) - Google Services + Firebase plugins, Kotlin, Java 8 desugaring
iOS
- Bundle ID:
com.bandraroad.rosieai - Display name: heyRosie AI / heyrosie
Info.plistpermissions: camera, photos, microphone, location, calendar
Linting
flutter_lints enabled in analysis_options.yaml. prefer_const_constructors is intentionally disabled — flipping it on requires a project-wide cleanup; do not do in a single PR.
Codegen
build_runner ^2.4.13 + mobx_codegen ^2.6.2 (theme observables).
Assets
assets/{fonts,svgs,images,data,chatscreen}/ — fonts include Inter, Geist, serif variants.
CI/CD
No GitHub Actions present. Builds are manual. Adding lint + tests + build per PR is on the backlog.
§12Testing
Coverage is intentionally light — the AI testing harness in /test_runner does the heavy E2E lifting.
test/
├── unit/
│ ├── authentication_provider_test.dart // pure error-mapping logic
│ ├── api_urls_test.dart // env switching
│ └── journal_api_test.dart
├── widget/
│ ├── dashboard_navigation_test.dart
│ ├── splash_screen_test.dart
│ └── screenshot_test.dart
├── collaborator_service_test.dart
└── widget_test.dart
integration_test/
├── app_flow_test.dart // captures screenshots
└── real_app_test.dart // full flow with Firebase
- Unit test for any new provider's pure logic
- Widget test for any new screen's initial render + error state
- Don't add integration tests without checking — they need the emulator and the AI harness
§13Conventions
- Classes
PascalCase· filessnake_case· vars/fnscamelCase - Feature-folder layout:
screens / provider / models / services / widgets - Public APIs use
///doc comments. Otherwise minimal comments. - Some
index.dartfiles act as barrel exports (e.g.lib/widgets/index.dart) - Provider consumption:
context.read<T>()/context.watch<T>()/Provider.of<T>(context, listen: false)
§14Known gotchas
android/gradle/wrapper/gradle-wrapper.properties) rejects JDK 26.
Always export JAVA_HOME=$(/usr/libexec/java_home -v 17) before flutter build apk.
Failure signature: "Error resolving plugin [id: 'dev.flutter.flutter-plugin-loader'…] > 26".
main.dart:139. Swapping environments today means editing source. Should be Remote Config or build-time env (see backlog).
premission_service.dart filename typo
Don't rename in isolation — touches many imports across the app.
JournalProvider and MemoryBuilderProvider are large & mix concerns
Don't grow them. If your change adds >50 lines, split first.
/connect-services
Keep that route handler intact.
main.dart. Test on Samsung if touching startup.
memoryId, deepLink, route. See main.dart:216–290.
adb install errors with INSTALL_FAILED_UPDATE_INCOMPATIBLE, uninstall first:
adb -s emulator-5554 uninstall com.bandraroad.heyrosie
§15Tech debt & backlog
Surfaced from the architecture audit. Source of truth lives in Frontend-Plan/backlog.md — this section mirrors it for reference.
| Item | Size | Area |
|---|---|---|
Split JournalProvider (500+ lines) — separate UI state from data layer | M | journal |
Untangle MemoryBuilderProvider — currently mixes memory build, Firestore user data, OneSignal token | M | memory_builder |
Introduce a structured logger; replace scattered print() / debugPrint() | S | core |
Move OneSignal App ID off main.dart:139 hardcode — Remote Config or build-time env | S | core |
| Reconcile version drift: pubspec 1.0.16+439 vs gradle 1.0.23/446 | S | build |
Dedup user-profile state between AuthenticationProvider and ProfileDataProvider | M | auth |
Adopt a consistent API result envelope (ApiResult<T>); standardise error messaging | L | core |
Rename premission_service.dart → permission_service.dart | S | core |
One-off cleanup pass to enable prefer_const_constructors | L | build |
| Add unit tests for the largest providers | M | testing |
| Wire up the Biographer feature (currently a "coming soon" stub) | L | biographer |
| Set up CI (lint + tests + build per PR) | M | build |
Pin Android NDK to 28.2.13676358 in android/app/build.gradle (integration_test plugin requires it; only a warning today) | S | build |
§16Investor / mentor Q&A
Likely questions from investors and engineering mentors, with honest answers framed for credibility.
Source of truth: Frontend-Plan/talking-points.md — edit there.
Architecture & technical choices
Q · Why Flutter and not native iOS + Android?
- One codebase, two platforms. A small team would otherwise need an iOS dev + an Android dev or accept being on one platform for 6–12 months longer.
- Performance is native-grade for a content-driven app like ours (no heavy 3D, no AR).
- Flutter is Google-backed — production at BMW, eBay, Alibaba, Google Pay.
- Trade-off, honestly: Flutter adds 8–12 MB to install size vs. pure native, and some niche native SDKs are slower to integrate. Neither has bitten us.
Q · Walk me through how the app is structured.
- UI —
lib/features/has one folder per feature (journal, memory builder, chat, …). Self-contained. - State management — Provider pattern. Each feature has one or two "Providers" holding its data; UI rebuilds when data changes.
- Services —
lib/core/services/is the only layer that talks to Firebase, our REST APIs, or the local cache. UI never touches the network directly.
Visual: architecture diagram, §1.
Q · Why Provider for state, not Riverpod or Bloc?
- Provider is the official Flutter team recommendation — the safest pick.
- Riverpod is newer and arguably better but the migration cost wasn't worth it.
- Bloc is heavier — designed for very large teams with strict separation of concerns. Overkill for our size.
- Honest gap: two providers grew too large and mix UI state with data. Logged on the backlog; not user-facing.
Q · What database do you use?
- Firestore (Google's NoSQL DB) for shared user data: profiles, memories, journals, connections.
- SQLite on-device for offline cache — app stays usable on a flight.
- Firebase Storage for photos and videos.
- No SQL/Postgres anywhere. Firestore gives us real-time sync, offline support, and authentication for free.
Q · How does authentication work?
Firebase Auth — email/password, Google, Apple, or Facebook. Sessions persisted. Firebase ID tokens (signed by Google) used when talking to our own backend. No passwords ever touch our servers.
Velocity & team productivity
Q · How fast can you ship a UI change?
- Small new screen: half a day to a day.
- Multi-screen feature (e.g. the memory-sharing flow): 2–5 days.
- Small visual bug fix to debug APK: target 15–30 minutes via a designed "screenshot → APK" workflow (§2).
Q · How do you know a build is safe to ship?
flutter analyze— static analysis on every build- Unit and widget tests for the riskiest pure logic (auth, API config, journaling)
- AI testing harness — separate system that runs the real APK on a real Android emulator and uses GPT-4o vision to navigate the UI like a user would
Honest gap: frontend unit-test coverage is intentionally light — we offloaded broad coverage to the E2E harness because it's more representative. Trade-off is a slower feedback loop. CI pipeline that runs both layers per PR is on the roadmap.
Q · How big is the engineering team?
Frontend is currently a single developer + AI-assisted coding (Claude Code). Architecture and tooling are explicitly set up so a second developer can onboard in a day — top-level handoff doc plus per-folder CLAUDE.md conventions.
Scalability & cost
Q · Will this scale to a million users?
- Flutter scales linearly — the app runs on the user's device, so each new user is "free" from a compute perspective.
- Firestore scales horizontally to billions of documents; pricing scales predictably with reads / writes / storage.
- Hot spots to watch: photo uploads (Storage egress is the largest variable cost), Firestore reads on feeds (we cache aggressively).
- What we'd change at scale: a Redis-style read-side cache in front of Firestore for the hottest queries. Today we don't need it.
Q · What's the riskiest dependency?
- Firebase (Google) — Firestore and Auth are very mature; risk is low.
- OneSignal (push) — easily swappable for FCM.
- socket.io for real-time — could swap for Firestore listeners.
- No single-vendor "if they go down we're out of business" dependency other than Google itself.
Q · What if Google raises Firebase prices?
Firestore reads / writes are individually cheap; meaningful cost is storage at scale. Migration to self-hosted Postgres + S3 is a 6–8 week project if ever forced — not painless, not existential. Today, costs are well under $200/month all-in.
Quality & maintainability
Q · What's the worst part of the codebase right now?
Three honest items, all tracked in §15 backlog:
- Two providers (
JournalProvider,MemoryBuilderProvider) are too large and mix concerns. Refactor is scoped. - No CI pipeline yet — builds are manual. Test scripts exist; need to wire them into GitHub Actions.
- Version-number drift between
pubspec.yamland the Android Gradle config. Cosmetic but should pick a source of truth before the first App Store release.
None are user-facing or block shipping.
Q · What happens when this developer gets hit by a bus?
- Full architecture documented in
architecture.mdand this HTML doc. - Every Flutter session loads an
AppCode/CLAUDE.mdencoding the rules. - A new Flutter dev (or another AI assistant) can be productive day one.
- A proposed screenshot → APK workflow (designed, prototype-validated) is intended to let non-engineers request fixes; once in daily use it further lowers the bus-factor risk.
Q · How are user-reported bugs handled?
Today: standard developer triage — Slack/email → repro → fix → next release. Nothing unusual.
Proposed (in design, not yet daily-use): the screenshot → APK workflow (§2) lets a non-engineer drop a screenshot + one sentence and produces a debug APK in 15–30 minutes. Validated once end-to-end; not yet the default for incoming bugs.
Production releases follow normal cadence (TestFlight / Play Store internal → external).
Security & privacy
Q · How is user data protected?
- In transit: HTTPS everywhere; Firebase ID tokens for our APIs.
- At rest: Firestore + Firebase Storage encrypted by Google.
- On device: Firebase Auth tokens, biometric unlock supported.
- Analytics consent-gated — we don't track without permission.
- App Check enabled — Firebase rejects requests from non-genuine app installs.
- No raw passwords on our infrastructure (Firebase Auth handles it).
Roadmap (frontend angle)
Q · What's the next major frontend investment?
- Biographer feature — currently a "coming soon" stub. Voice + text life-story capture.
- CI pipeline — automated lint + test + build per PR.
- Provider refactor — split the two large ones; cleaner foundation for new contributors.
- iOS-side polish — Flutter handles both platforms but iOS-specific UX touches (haptics, dynamic type) deserve a pass before App Store launch.
Q · What can the frontend NOT do today that you wish it could?
- Real-time collaborative memory editing — architecturally possible (socket.io is in place) but no UI yet.
- Rich text editing in journal (today it's mostly plain text + attachments).
- Native-feeling iOS scroll physics in some screens.
- This HTML doc — six diagrams + reference for every layer
workflow.md+ the debug APK inbuilds/— the proposed loop, validated end-to-end oncebacklog.md— every known issue logged. Transparency is a feature.- The running app on the emulator — already installed, can demo end-to-end
- "Production-ready test coverage" — intentionally light on frontend; be honest about the trade-off.
- "Scales to millions today" — designed to, hasn't been load-tested. Say "designed to scale."
- "No tech debt" — every codebase has it; ours is logged and triaged.
- Don't oversell the AI testing harness as fully autonomous — powerful but benefits from human review.
- Don't call the screenshot → APK workflow "our standard practice." It's proposed and prototype-validated. Say "designed, validated end-to-end once, rolling out."
§17Quick reference
Where things live
| Concern | Class | File |
|---|---|---|
| App entry | MyApp | lib/main.dart:325 |
| Auth | AuthenticationProvider | lib/providers/authentication_provider.dart |
| Theme | ThemeStore | lib/features/settings/theme_store.dart |
| Routing | AppRouter | lib/core/navigation/app_router.dart |
| Home | Dashboard | lib/features/home/dashboard.dart |
| Journal | JournalProvider / JournalPage | lib/features/journal/... |
| Memory build | NewMemoryBuilder / MemoryBuilderProvider | lib/features/memory_builder/... |
| Analytics | AnalyticsService | lib/core/services/analytics_service.dart |
| Firebase | FirebaseService | lib/core/services/firebase_service.dart |
| Notifications | NotificationService | lib/core/services/notification_service.dart |
| DI | locator | lib/core/di/locator.dart |
| Splash | SplashScreen | lib/features/splash/splash_screen.dart |
| Connections | ConnectionProvider | lib/features/Connections/provider/connection_provider.dart |
| API config | ApiUrls | lib/core/config/api_urls.dart |
Companion documents
Frontend-Plan/README.md— folder map & workflowsFrontend-Plan/workflow.md— full screenshot → APK playbookFrontend-Plan/architecture.md— markdown mirror of this docFrontend-Plan/backlog.md— open work (source of truth)Frontend-Plan/talking-points.md— investor & mentor Q&A (editable source)AppCode/CLAUDE.md— rules every Claude session loads when working inAppCode/