Skip to main content
Glama
mobile-cross-platform.mdc14.4 kB
--- description: "Cross-platform mobile development: shared architecture, platform-specific patterns, and native integration strategies" alwaysApply: false --- # Cross-Platform Mobile Development Strategies and patterns for building mobile applications that work across iOS, Android, and other platforms. ## CRITICAL: Agentic-First Mobile Development ### Pre-Development Verification (MANDATORY) Before writing ANY mobile code: ``` 1. CHECK CLI AVAILABILITY → run_terminal_cmd("flutter --version") // For Flutter → run_terminal_cmd("npx react-native --version") // For RN → run_terminal_cmd("dotnet --list-sdks") // For MAUI 2. VERIFY CURRENT VERSIONS (use web_search) → web_search("Flutter stable version December 2024") → web_search("React Native latest version 2024") 3. CHECK EXISTING PROJECT → Does pubspec.yaml/package.json exist? → Are dependencies already installed? → Is this a new project or modification? 4. USE CLI FOR PROJECT CREATION (Never create files manually!) Flutter: flutter create my_app React Native: npx react-native init MyApp Expo: npx create-expo-app@latest my-app ``` ### CLI-First Development Rules **NEVER manually create:** - `pubspec.yaml` from scratch (use `flutter create`) - `package.json` for RN (use `npx react-native init`) - iOS/Android project files (generated by CLI) - Platform-specific configurations **ALWAYS use CLI:** ```bash # Flutter flutter create my_app flutter pub add provider flutter pub add go_router flutter build apk # React Native npx react-native init MyApp npm install @react-navigation/native npx react-native run-android # Expo npx create-expo-app my-app npx expo install expo-camera npx expo start ``` ### Post-Setup Verification After project creation, ALWAYS verify: ```bash # Flutter cd my_app && flutter pub get && flutter analyze # React Native cd MyApp && npm install && npx react-native doctor # Expo cd my-app && npm install && npx expo doctor ``` --- ## Architecture Decisions ### When to Use Cross-Platform **Good Fit:** - Content-focused apps (news, social, e-commerce) - Business/productivity applications - MVPs and rapid prototyping - Teams with web development background - Apps with similar UI across platforms **Consider Native When:** - Heavy platform-specific features (AR, advanced camera) - Performance-critical applications (games, video editing) - Deep OS integration required - Platform-specific design language is critical - Small, focused utility apps ### Framework Selection | Framework | Best For | Trade-offs | |-----------|----------|------------| | **Flutter** | Beautiful custom UI, performance | Larger app size, Dart learning curve | | **React Native** | Web team transition, code sharing | Bridge overhead, native module complexity | | **Kotlin Multiplatform** | Shared business logic, native UI | Newer ecosystem, iOS tooling | | **.NET MAUI** | Microsoft ecosystem, enterprise | Smaller community, platform coverage | --- ## Shared Architecture Patterns ### Clean Architecture for Mobile ``` ┌─────────────────────────────────────┐ │ Presentation Layer │ │ (Platform-specific UI/Framework) │ ├─────────────────────────────────────┤ │ Domain Layer │ │ (Use Cases, Entities, Interfaces) │ │ ★ SHARED CODE ★ │ ├─────────────────────────────────────┤ │ Data Layer │ │ (Repositories, Data Sources) │ │ Partially Shared │ └─────────────────────────────────────┘ ``` ### Code Sharing Strategy **Share:** - Business logic and use cases - Data models and entities - API client and networking - Validation rules - State management logic - Utility functions **Keep Platform-Specific:** - UI components and layouts - Navigation implementation - Platform permissions - Native API integrations - Animations (unless framework handles well) --- ## State Management ### Unified State Architecture ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Action │ ──► │ Reducer/ │ ──► │ State │ │ (Intent) │ │ Handler │ │ (Store) │ └─────────────┘ └──────────────┘ └─────────────┘ │ ▼ ┌──────────────┐ │ Side Effects │ │ (API calls) │ └──────────────┘ ``` ### State Patterns by Framework **Flutter (BLoC/Riverpod):** ```dart // Event → Bloc → State class UserBloc extends Bloc<UserEvent, UserState> { UserBloc(this.repository) : super(UserInitial()) { on<LoadUser>(_onLoadUser); } } ``` **React Native (Redux/Zustand):** ```typescript // Action → Reducer → Store const userSlice = createSlice({ name: 'user', initialState, reducers: { setUser: (state, action) => { state.user = action.payload; }, }, }); ``` **Kotlin Multiplatform (MVI):** ```kotlin // Intent → ViewModel → State class UserViewModel : ViewModel() { private val _state = MutableStateFlow(UserState()) val state: StateFlow<UserState> = _state.asStateFlow() fun loadUser(id: String) { viewModelScope.launch { _state.update { it.copy(loading = true) } val user = repository.getUser(id) _state.update { it.copy(user = user, loading = false) } } } } ``` --- ## Navigation Patterns ### Deep Linking Architecture ``` URL: myapp://product/123?source=push ┌─────────────────────────────────────┐ │ Deep Link Handler │ │ Parse URL → Extract parameters │ ├─────────────────────────────────────┤ │ Route Resolver │ │ Map to internal route/screen │ ├─────────────────────────────────────┤ │ Navigation Stack │ │ Build appropriate back stack │ └─────────────────────────────────────┘ ``` ### Navigation Best Practices 1. **Centralized Route Definitions** - Define all routes in one place - Use typed route parameters - Handle unknown routes gracefully 2. **Deep Link Support** - Configure URL schemes (iOS) and intent filters (Android) - Handle universal/app links - Preserve navigation state 3. **Back Stack Management** - Maintain logical back navigation - Handle authentication flows - Support nested navigation --- ## Platform Integration ### Feature Detection Pattern ``` ┌─────────────────────────────────────┐ │ Feature Interface │ │ abstract void requestPermission() │ │ abstract bool isSupported() │ ├──────────────┬──────────────────────┤ │ iOS Impl │ Android Impl │ │ (Native) │ (Native) │ └──────────────┴──────────────────────┘ ``` ### Common Platform Differences | Feature | iOS | Android | |---------|-----|---------| | **Permissions** | Info.plist + runtime | Manifest + runtime | | **Push Notifications** | APNs | FCM | | **Background Tasks** | Limited (BGTaskScheduler) | WorkManager, Services | | **Storage** | Keychain, UserDefaults | SharedPrefs, Keystore | | **Camera** | AVFoundation | Camera2/CameraX | | **Location** | CoreLocation | FusedLocationProvider | ### Handling Platform Differences ```dart // Flutter example if (Platform.isIOS) { // iOS-specific implementation } else if (Platform.isAndroid) { // Android-specific implementation } // Better: Use abstraction abstract class BiometricAuth { Future<bool> authenticate(); factory BiometricAuth() { if (Platform.isIOS) return IOSBiometricAuth(); return AndroidBiometricAuth(); } } ``` --- ## Networking ### Offline-First Architecture ``` ┌─────────────────────────────────────┐ │ UI Layer │ ├─────────────────────────────────────┤ │ Repository │ │ ┌─────────────┐ ┌──────────────┐ │ │ │ Local Cache │◄─│ Remote API │ │ │ │ (SQLite/ │ │ │ │ │ │ Realm) │ │ │ │ │ └─────────────┘ └──────────────┘ │ ├─────────────────────────────────────┤ │ Sync Manager │ │ Queue changes, retry, conflict res │ └─────────────────────────────────────┘ ``` ### Caching Strategy ```dart class UserRepository { final ApiClient api; final LocalDatabase db; Future<User> getUser(String id) async { // 1. Return cached immediately final cached = await db.getUser(id); if (cached != null) { // 2. Refresh in background _refreshUser(id); return cached; } // 3. Fetch if no cache final user = await api.getUser(id); await db.saveUser(user); return user; } Future<void> _refreshUser(String id) async { try { final user = await api.getUser(id); await db.saveUser(user); } catch (e) { // Log but don't fail } } } ``` --- ## Performance Optimization ### List Performance - **Virtualization**: Only render visible items - **Image Optimization**: Resize, cache, lazy load - **Pagination**: Load data incrementally - **Placeholder Skeletons**: Show loading state per item ### Memory Management ```dart // Dispose resources @override void dispose() { _controller.dispose(); _subscription.cancel(); super.dispose(); } // Cache management class ImageCache { final _cache = LruCache<String, Image>(maxSize: 100); void clear() => _cache.clear(); } ``` ### Startup Optimization 1. **Defer initialization**: Load non-critical features lazily 2. **Splash screen**: Show immediately while loading 3. **Code splitting**: Load features on demand 4. **Preload critical data**: Fetch during splash --- ## Testing Strategy ### Test Pyramid for Mobile ``` /\ / \ E2E Tests (5-10%) / \ - Critical user flows /------\ Integration Tests (20-30%) / \ - Screen + ViewModel /----------\ Unit Tests (60-70%) / \ - Business logic ``` ### Platform-Specific Testing ```dart // Shared test void main() { group('UserRepository', () { test('fetches user from API', () async { final repo = UserRepository(mockApi, mockDb); final user = await repo.getUser('1'); expect(user.name, 'Test'); }); }); } // Platform-specific test @TestOn('ios') void main() { testWidgets('shows iOS-style picker', (tester) async { // iOS-specific UI test }); } ``` --- ## Release & Distribution ### Version Strategy ``` Version: 1.2.3+45 │ │ │ │ │ │ │ └── Build number (increments every build) │ │ └───── Patch (bug fixes) │ └─────── Minor (new features, backward compatible) └───────── Major (breaking changes) ``` ### CI/CD Pipeline ```yaml # Simplified pipeline stages: - test: - lint - unit_tests - integration_tests - build: - build_android_debug - build_ios_debug - deploy_staging: - deploy_android_internal - deploy_ios_testflight - deploy_production: - deploy_android_production - deploy_ios_appstore ``` ### App Store Considerations **iOS App Store:** - Screenshots for all device sizes - App Preview videos - Privacy labels required - Review guidelines compliance **Google Play:** - Feature graphic required - Data safety section - Target API level requirements - In-app update support --- ## Common Patterns ### Pull-to-Refresh ```dart RefreshIndicator( onRefresh: () async { await viewModel.refresh(); }, child: ListView.builder(...), ) ``` ### Infinite Scroll ```dart class PaginatedList extends StatelessWidget { @override Widget build(BuildContext context) { return NotificationListener<ScrollNotification>( onNotification: (notification) { if (notification.metrics.pixels >= notification.metrics.maxScrollExtent - 200) { viewModel.loadMore(); } return false; }, child: ListView.builder(...), ); } } ``` ### Error Handling UI ```dart Widget buildContent(BuildContext context, AsyncValue<Data> state) { return state.when( loading: () => const LoadingWidget(), error: (error, stack) => ErrorWidget( message: error.toString(), onRetry: () => ref.refresh(dataProvider), ), data: (data) => DataWidget(data: data), ); } ``` ### Empty State ```dart if (items.isEmpty) { return EmptyState( icon: Icons.inbox, title: 'No items yet', message: 'Add your first item to get started', action: ElevatedButton( onPressed: onAddItem, child: Text('Add Item'), ), ); } ```

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/madebyaris/rakitui-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server