|
| 1 | +# Mobile App Standards |
| 2 | + |
| 3 | +## ⚠️ CRITICAL RULES |
| 4 | + |
| 5 | +- **DO NOT assume a mobile app exists** - only build one when explicitly requested |
| 6 | +- **Flutter is the DEFAULT framework** for all mobile applications - no exceptions unless stated |
| 7 | +- **Target BOTH iOS and Android** - every mobile app ships on both platforms |
| 8 | +- **Support phones AND tablets** - all layouts must be responsive across phone and tablet form factors on both platforms |
| 9 | +- **Native modules are permitted** only when Flutter lacks support for a specific capability |
| 10 | +- **Dart linting required** - all code must pass `flutter analyze` before commit |
| 11 | +- **Platform testing required** - test on iOS and Android devices/emulators for both phone and tablet screen sizes |
| 12 | + |
| 13 | +## When to Build a Mobile App |
| 14 | + |
| 15 | +**Only build a mobile app when the user or project requirements explicitly call for one.** Mobile apps are not part of the default project template. The standard three-container architecture (WebUI, API, Go backend) does not include a mobile client. |
| 16 | + |
| 17 | +**Indicators that a mobile app is needed:** |
| 18 | +- User explicitly requests a mobile app |
| 19 | +- Requirements document specifies iOS/Android support |
| 20 | +- Project scope includes native device features (push notifications, camera, biometrics, etc.) |
| 21 | + |
| 22 | +**If unclear, ask before creating any mobile app scaffolding.** |
| 23 | + |
| 24 | +## Technology Stack |
| 25 | + |
| 26 | +**Framework: Flutter (Default)** |
| 27 | +- Flutter SDK (latest stable channel) |
| 28 | +- Dart language (version aligned with Flutter SDK) |
| 29 | +- Cross-platform: single codebase for iOS and Android |
| 30 | + |
| 31 | +**When to Use Native Modules:** |
| 32 | +- Flutter has no plugin or package for the required functionality |
| 33 | +- Existing Flutter plugins are unmaintained, unstable, or lack critical features |
| 34 | +- Performance-critical operations that require direct platform API access (e.g., low-level Bluetooth, custom camera pipelines, real-time audio processing) |
| 35 | +- Platform-specific APIs with no Flutter equivalent (e.g., certain HealthKit/Health Connect features, NFC advanced modes, platform-specific accessibility APIs) |
| 36 | + |
| 37 | +**Native module approach:** |
| 38 | +- Use Flutter platform channels (`MethodChannel`, `EventChannel`) to bridge native code |
| 39 | +- Write native code in Swift for iOS, Kotlin for Android |
| 40 | +- Keep native code minimal - only what Flutter cannot do |
| 41 | +- Document every native module with justification for why Flutter was insufficient |
| 42 | + |
| 43 | +## Project Structure |
| 44 | + |
| 45 | +``` |
| 46 | +services/mobile/ |
| 47 | +├── lib/ |
| 48 | +│ ├── main.dart # App entry point |
| 49 | +│ ├── app.dart # App widget, routing, theme |
| 50 | +│ ├── config/ # Environment, constants |
| 51 | +│ ├── models/ # Data models |
| 52 | +│ ├── services/ # API client, auth, storage |
| 53 | +│ ├── providers/ # State management |
| 54 | +│ ├── screens/ # Page-level widgets |
| 55 | +│ ├── widgets/ # Reusable UI components |
| 56 | +│ └── utils/ # Helpers, extensions |
| 57 | +├── android/ # Android native project |
| 58 | +├── ios/ # iOS native project |
| 59 | +├── test/ # Unit and widget tests |
| 60 | +├── integration_test/ # Integration tests |
| 61 | +├── pubspec.yaml # Dependencies |
| 62 | +├── analysis_options.yaml # Lint rules |
| 63 | +└── Dockerfile # CI build environment |
| 64 | +``` |
| 65 | + |
| 66 | +## Platform & Device Support |
| 67 | + |
| 68 | +**Platforms:** |
| 69 | +| Platform | Language (Native Modules) | Min Version | |
| 70 | +|----------|--------------------------|-------------| |
| 71 | +| iOS | Swift | iOS 15+ | |
| 72 | +| Android | Kotlin | API 24+ (Android 7.0) | |
| 73 | + |
| 74 | +**Form Factors:** |
| 75 | +| Device | Breakpoint | Layout Expectations | |
| 76 | +|---------|---------------|---------------------| |
| 77 | +| Phone | < 600dp wide | Single-column, bottom navigation | |
| 78 | +| Tablet | >= 600dp wide | Multi-pane, side navigation, master-detail | |
| 79 | + |
| 80 | +**Responsive layout is mandatory.** Use `LayoutBuilder` or `MediaQuery` to adapt UI across form factors. Never hardcode widths or assume a single device size. |
| 81 | + |
| 82 | +## API Integration |
| 83 | + |
| 84 | +The mobile app communicates with the same Flask backend API used by the WebUI: |
| 85 | + |
| 86 | +```dart |
| 87 | +// lib/services/api_client.dart |
| 88 | +import 'package:dio/dio.dart'; |
| 89 | +
|
| 90 | +class ApiClient { |
| 91 | + final Dio _dio; |
| 92 | +
|
| 93 | + ApiClient({required String baseUrl}) |
| 94 | + : _dio = Dio(BaseOptions( |
| 95 | + baseUrl: baseUrl, |
| 96 | + headers: {'Content-Type': 'application/json'}, |
| 97 | + )) { |
| 98 | + _dio.interceptors.add(InterceptorsWrapper( |
| 99 | + onRequest: (options, handler) { |
| 100 | + final token = AuthService.instance.token; |
| 101 | + if (token != null) { |
| 102 | + options.headers['Authorization'] = 'Bearer $token'; |
| 103 | + } |
| 104 | + handler.next(options); |
| 105 | + }, |
| 106 | + onError: (error, handler) { |
| 107 | + if (error.response?.statusCode == 401) { |
| 108 | + AuthService.instance.logout(); |
| 109 | + } |
| 110 | + handler.next(error); |
| 111 | + }, |
| 112 | + )); |
| 113 | + } |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +**API versioning:** Use the same `/api/v{major}/endpoint` pattern as web clients. |
| 118 | + |
| 119 | +## State Management |
| 120 | + |
| 121 | +**Recommended:** `provider` or `riverpod` for state management. Choose one per project and use it consistently. |
| 122 | + |
| 123 | +- `provider` - simpler apps with straightforward state |
| 124 | +- `riverpod` - complex apps needing compile-safe dependency injection |
| 125 | + |
| 126 | +## Authentication |
| 127 | + |
| 128 | +Use the same Flask-Security-Too backend as the WebUI. The mobile app should support: |
| 129 | +- JWT token storage (secure storage, not shared preferences) |
| 130 | +- Biometric authentication (fingerprint, face) via `local_auth` plugin |
| 131 | +- MFA/2FA support matching the web flow |
| 132 | +- Token refresh on 401 responses |
| 133 | + |
| 134 | +**Secure storage:** |
| 135 | +```dart |
| 136 | +// Use flutter_secure_storage for tokens - never SharedPreferences |
| 137 | +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; |
| 138 | +
|
| 139 | +final storage = FlutterSecureStorage(); |
| 140 | +await storage.write(key: 'authToken', value: token); |
| 141 | +``` |
| 142 | + |
| 143 | +## Design & Theming |
| 144 | + |
| 145 | +Follow the same design language as the WebUI where appropriate: |
| 146 | +- Dark theme default with gold/amber accents |
| 147 | +- Use `ThemeData` for consistent styling |
| 148 | +- Material Design 3 as the base design system |
| 149 | +- Platform-adaptive widgets where appropriate (Cupertino on iOS for native feel is acceptable) |
| 150 | + |
| 151 | +**Tablet layouts must differ from phone layouts.** Use adaptive layouts: |
| 152 | +- Phone: bottom navigation bar, single-column content |
| 153 | +- Tablet: navigation rail or side drawer, multi-pane layouts, master-detail patterns |
| 154 | + |
| 155 | +## Testing |
| 156 | + |
| 157 | +**Required test coverage:** |
| 158 | +- Unit tests for models, services, and business logic |
| 159 | +- Widget tests for UI components |
| 160 | +- Integration tests for critical user flows |
| 161 | +- Test on both phone and tablet emulators for each platform |
| 162 | + |
| 163 | +```bash |
| 164 | +# Run all tests |
| 165 | +flutter test |
| 166 | + |
| 167 | +# Run integration tests |
| 168 | +flutter test integration_test/ |
| 169 | + |
| 170 | +# Analyze code |
| 171 | +flutter analyze |
| 172 | +``` |
| 173 | + |
| 174 | +**Device matrix for testing:** |
| 175 | +| Platform | Phone Emulator | Tablet Emulator | |
| 176 | +|----------|----------------------|----------------------| |
| 177 | +| iOS | iPhone 15 | iPad Pro 12.9" | |
| 178 | +| Android | Pixel 8 | Pixel Tablet | |
| 179 | + |
| 180 | +## Build & Distribution |
| 181 | + |
| 182 | +```bash |
| 183 | +# Build for both platforms |
| 184 | +flutter build apk --release # Android APK |
| 185 | +flutter build appbundle --release # Android App Bundle (Play Store) |
| 186 | +flutter build ipa --release # iOS (requires macOS) |
| 187 | +``` |
| 188 | + |
| 189 | +**CI builds** use a Debian-based Docker image with Flutter SDK for Android builds. iOS builds require a macOS runner (GitHub Actions `macos-latest`). |
| 190 | + |
| 191 | +## Linting & Code Quality |
| 192 | + |
| 193 | +**analysis_options.yaml:** |
| 194 | +```yaml |
| 195 | +include: package:flutter_lints/flutter.yaml |
| 196 | + |
| 197 | +linter: |
| 198 | + rules: |
| 199 | + prefer_const_constructors: true |
| 200 | + prefer_const_literals_to_create_immutables: true |
| 201 | + avoid_print: true |
| 202 | + prefer_single_quotes: true |
| 203 | + sort_child_properties_last: true |
| 204 | + use_build_context_synchronously: true |
| 205 | +``` |
| 206 | +
|
| 207 | +**Run before every commit:** |
| 208 | +```bash |
| 209 | +flutter analyze # Static analysis |
| 210 | +dart format --set-exit-if-changed . # Formatting |
| 211 | +flutter test # All tests |
| 212 | +``` |
| 213 | + |
| 214 | +## Native Module Guidelines |
| 215 | + |
| 216 | +When a native module is necessary: |
| 217 | + |
| 218 | +1. **Document the justification** - add a comment in the native code and a note in the project's `APP_STANDARDS.md` explaining why Flutter alone is insufficient |
| 219 | +2. **Keep native code minimal** - only implement what Flutter cannot handle; all other logic stays in Dart |
| 220 | +3. **Use platform channels** - `MethodChannel` for request/response, `EventChannel` for streams |
| 221 | +4. **Write for both platforms** - every native module must have both Swift (iOS) and Kotlin (Android) implementations |
| 222 | +5. **Test native code** - include platform-specific tests (XCTest for iOS, JUnit for Android) |
| 223 | + |
| 224 | +**Example platform channel:** |
| 225 | +```dart |
| 226 | +// lib/services/native_bridge.dart |
| 227 | +import 'package:flutter/services.dart'; |
| 228 | +
|
| 229 | +class NativeBridge { |
| 230 | + static const _channel = MethodChannel('com.penguintech.app/native'); |
| 231 | +
|
| 232 | + static Future<String?> getPlatformSpecificData() async { |
| 233 | + return await _channel.invokeMethod('getPlatformSpecificData'); |
| 234 | + } |
| 235 | +} |
| 236 | +``` |
| 237 | + |
| 238 | +## Security |
| 239 | + |
| 240 | +- Store tokens in platform secure storage (Keychain on iOS, EncryptedSharedPreferences on Android) |
| 241 | +- Enable certificate pinning for API connections in production |
| 242 | +- Obfuscate release builds (`flutter build apk --obfuscate --split-debug-info=build/debug-info`) |
| 243 | +- No hardcoded secrets, API keys, or credentials in Dart or native code |
| 244 | +- Use environment configuration for API URLs and feature flags |
0 commit comments