ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Cross-Platform Mobile Development

Choose and implement the right cross-platform mobile framework. Covers React Native, Flutter, Kotlin Multiplatform, architecture differences, performance implications, native bridge patterns, and the decision framework for platform selection.

Cross-platform mobile development promises one codebase for iOS and Android. The reality is more nuanced — frameworks differ dramatically in architecture, performance characteristics, and native integration depth. Choosing the wrong framework can cost more than building natively.


Framework Comparison

AspectReact NativeFlutterKotlin Multiplatform
LanguageJavaScript/TypeScriptDartKotlin
UI renderingNative componentsCustom (Skia/Impeller)Native (shared logic only)
Code sharing90-95% (including UI)95-98% (including UI)50-70% (logic only)
PerformanceNear-native (with JSI)Near-nativeNative
Hot reloadYesYes (faster)Limited
App size+7MB baseline+5MB baseline+1-2MB
EcosystemNPM (massive)pub.dev (growing)Maven/Gradle (mature)
Learning curveLow (if JS/React)Medium (new language)Medium (if Kotlin)
CompanyMetaGoogleJetBrains

React Native (New Architecture)

// React Native with JSI (direct native binding, no bridge)
import { TurboModuleRegistry, TurboModule } from 'react-native';

// New Architecture: Fabric (UI) + TurboModules (native)
interface Spec extends TurboModule {
  getDeviceId(): string;
  processPayment(amount: number, currency: string): Promise<PaymentResult>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('PaymentModule');

// Component with new Fabric renderer
const OrderCard: React.FC<{ order: Order }> = ({ order }) => {
  return (
    <View style={styles.card}>
      <Text style={styles.title}>Order #{order.id}</Text>
      <Text style={styles.amount}>${order.total.toFixed(2)}</Text>
      <Pressable
        style={styles.button}
        onPress={() => handleConfirm(order)}
        android_ripple={{ color: 'rgba(0,0,0,0.1)' }}
      >
        <Text style={styles.buttonText}>Confirm</Text>
      </Pressable>
    </View>
  );
};

Flutter

// Flutter: Custom rendering engine (Skia/Impeller)
class OrderScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Orders')),
      body: StreamBuilder<List<Order>>(
        stream: orderRepository.watchAll(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) return CircularProgressIndicator();
          
          return ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              final order = snapshot.data![index];
              return OrderCard(
                order: order,
                onConfirm: () => _confirmOrder(order),
              );
            },
          );
        },
      ),
    );
  }
}

// Platform channels for native functionality
class PaymentChannel {
  static const platform = MethodChannel('com.app/payment');
  
  Future<String> processPayment(double amount) async {
    final result = await platform.invokeMethod('processPayment', {
      'amount': amount,
      'currency': 'USD',
    });
    return result;
  }
}

Kotlin Multiplatform

// Shared module (used by both iOS and Android)
// Only business logic is shared, UI is native per platform
class OrderRepository(
    private val api: OrderApi,
    private val database: OrderDatabase,
    private val dispatcher: CoroutineDispatcher
) {
    suspend fun getOrders(): List<Order> = withContext(dispatcher) {
        try {
            val remote = api.fetchOrders()
            database.saveOrders(remote)
            remote
        } catch (e: Exception) {
            database.getOrders() // Offline fallback
        }
    }
    
    fun watchOrders(): Flow<List<Order>> = database.observeOrders()
}

// Expect/actual for platform-specific implementations
expect class PlatformPayment() {
    fun processPayment(amount: Double): PaymentResult
}

// Android: actual implementation
actual class PlatformPayment {
    actual fun processPayment(amount: Double): PaymentResult {
        // Google Pay integration
    }
}

// iOS: actual implementation (in Kotlin, compiled to iOS framework)
actual class PlatformPayment {
    actual fun processPayment(amount: Double): PaymentResult {
        // Apple Pay integration
    }
}

Decision Framework

Small team, web developers → React Native
  Leverage existing React/JS skills
  Large ecosystem, many libraries
  
Pixel-perfect custom UI, performance-critical → Flutter
  Custom rendering = identical UI on both platforms
  Best hot reload experience
  
Existing native apps, share business logic → Kotlin Multiplatform
  Keep native UI, share 50-70% logic
  Gradual adoption, no rewrite needed
  
Maximum native performance/UX → Native (Swift + Kotlin)
  Full platform API access, best performance
  2x development cost

Anti-Patterns

Anti-PatternConsequenceFix
Choosing cross-platform for 1 platformUnnecessary abstractionGo native if targeting single platform
Ignoring platform conventionsApp feels foreign on both platformsPlatform-specific UI adaptations
Too many native bridgesPerformance bottleneck, complexityBatch native calls, use new architecture
Cross-platform for heavy GPU/3DPoor performance, limitationsNative or game engines (Unity)
Not measuring real-world perfAssumptions instead of dataProfile on actual target devices

Cross-platform is not free — it trades native platform depth for development velocity. The right choice depends on your team, your app complexity, and how much platform-specific behavior matters.

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →