Mobile Application Architecture
The MQUAD mobile field app is an enterprise-grade cross-platform data collection system built using Flutter and Dart. Designed for critical public health, academic research, and socioeconomic survey campaigns, the app delivers a resilient offline-first runtime supporting Android, iOS, macOS, Windows, and Linux.
1. Architectural Principles
The application is architected around four core pillars:
- Offline Authority: The local device holds full operational capability without needing live network access. Interviews, media capture, skip logic, and validation evaluate with zero latency.
- Clean Architecture Separation: Strict boundaries between Presentation, Domain, and Data layers ensure business logic remains isolated from UI rendering and database mechanics.
- Multi-Tenant Iteration: The client authenticates once with multi-tenant token support (
tenant_id: nullin base payload) and iterates across assigned forms from multiple organizations seamlessly. - Defensive Encryption & Sovereignty: Sensitive research data is protected at rest with 256-bit AES SQLCipher encryption, cryptographic verification leases, and automated paradata logging.
2. Technology Stack
| Layer | Technology | Version | Purpose |
|---|---|---|---|
| Core Framework | Flutter SDK | ^3.41.0 | Multi-platform UI engine |
| Language | Dart SDK | ^3.11.0 | Typed, null-safe client runtime |
| State Management | Riverpod | ^3.3.1 | Compile-safe dependency injection & state notifiers |
| Local Database | Drift + SQLCipher | ^2.33.0 | Type-safe reactive SQLite with 256-bit AES encryption |
| Networking | Dio | ^5.7.0 | HTTP client with multi-tenant interceptors & retry logic |
| Routing | GoRouter | ^17.3.0 | Declarative URL-based and deep-link routing |
| Secure Key Storage | FlutterSecureStorage | ^10.2.0 | Keychain (iOS/macOS) and Keystore (Android) access |
| Offline Mapping | Flutter Map | ^7.0.2 | Leaflet-compatible interactive map tiles with offline caching |
| Audio & Speech | Flutter TTS + Record | Latest | Human audio playback with system TTS fallback |
3. Layered Directory Structure
mobile/lib/
├── constants/ # Global application constants and version flags
├── core/
│ ├── auth/ # Global auth interceptors and token contracts
│ ├── database/ # Drift database definitions, migrations, SQLCipher setup
│ ├── di/ # Core dependency providers
│ ├── errors/ # Typed app exceptions, failure wrappers, error mappers
│ ├── layouts/ # Responsive breakpoints, master/detail scaffolds
│ ├── network/ # Dio client setup, token refresh interceptor
│ ├── paradata/ # Sensor capture, interaction audit tracker
│ ├── router/ # GoRouter routing table and auth redirect guards
│ ├── security/ # Inactivity gates, screenshot blocker, biometric engines
│ └── theme/ # Design tokens (AppColors, AppTypography, DensityScope)
├── features/
│ ├── auth/ # Login, token refresh, biometric unlock, tenant hydration
│ ├── surveys/ # Survey catalog, version-pinning, pre-flight checks
│ ├── form/ # XLSForm runtime engine, input widgets, AST evaluator
│ ├── responses/ # Response viewing, correction mode, detail inspector
│ ├── sync/ # Background/foreground sync engine, conflict resolver
│ ├── media/ # Gallery, compression pipeline, resumable uploaders
│ └── updates/ # OTA binary updates, version compatibility checker
└── shared/
└── widgets/ # Atomic UI library (buttons, inputs, cards, dialogs)4. Feature Modularization Pattern
Each business domain under features/ adheres to Clean Architecture:
feature_name/
├── data/
│ ├── datasources/ # Remote API clients and local Drift DAO queries
│ ├── dtos/ # JSON serialization models (Freezed / json_serializable)
│ └── repositories/ # Repository implementations coordinating local & remote
├── domain/
│ ├── entities/ # Pure immutable business models
│ ├── repositories/ # Abstract repository interface contracts
│ └── usecases/ # Self-contained business operations
└── presentation/
├── providers/ # Riverpod Notifiers and StateNotifier providers
├── screens/ # Full-screen page widgets
└── widgets/ # Feature-specific atomic components5. Multi-Tenancy Resolution in Mobile
Unlike standard web applications where a user switches tenants via an active session selector, MQUAD mobile enumerators must see surveys across all authorized tenants simultaneously.
Multi-Tenant Token Flow
- Login (
/account/login_new): Returns a primary access token withtenant_id: nulland an array of authorized tenant IDs:tenant_ids: ["tenant-a-uuid", "tenant-b-uuid"]. - Dynamic Context Scoping: When requesting data for a specific survey, the repository wraps the call with
TenantRequestContext.runWithTenant(tenantId, ...). - Header Injection: The
AuthInterceptorreads the active request context and dynamically injects the required header:GET /surveys HTTP/1.1 Host: api.mquad.local Authorization: Bearer <multi_tenant_jwt> X-Tenant-ID: b94d513e-7993-4ecf-bd47-a2f6041623bb - Microservice Validation: The backend services verify that
tenant_idis eitherNone(multi-tenant token) or matchesX-Tenant-ID, and confirms that the requested tenant is present in the token'stenant_idsclaim.
6. Reactive State Architecture
Diagram source
graph TD
UI[Flutter UI Widgets] -->|Watch / Select| Notifier[Riverpod StateNotifier]
Notifier -->|Execute| UC[Domain UseCase]
UC -->|Query / Mutate| Repo[Repository Implementation]
Repo -->|Cached First| LocalDS[Local DataSource - Drift/SQLCipher]
Repo -->|Sync / Network| RemoteDS[Remote DataSource - Dio]
LocalDS -->|Streams| Notifier
Notifier -->|Rebuilds with new state| UIAll screens observe state via Riverpod providers. Changes made in the local database trigger reactive Drift streams (select().watch()), updating the UI in real time without manual polling.
