Volver al blog

Móvil

Nativo vs Multiplataforma: Perspectiva 2026

6 min lecturaEnviaIT Engineering

Después de lanzar más de 12 apps móviles en los últimos 5 años, hemos usado todos los enfoques: Swift/Kotlin nativo, React Native, Flutter, y hasta Ionic en los primeros días. Esta es nuestra perspectiva honesta, sin agendas ni fanatismo por ningún framework.

La respuesta rápida a "¿qué debería usar?" es depende. Pero "depende" no te ayuda a tomar decisiones, así que vamos a desglosar exactamente de qué depende.

El estado del arte en 2026

El ecosistema móvil ha madurado significativamente. Ya no estamos en la era de "nativo vs híbrido" con compromisos brutales. Las herramientas multiplataforma son legítimamente buenas y la brecha de rendimiento con nativo se ha cerrado al punto de ser irrelevante para la mayoría de apps.

Dicho esto, cada enfoque tiene un "sweet spot" claro. Usarlos fuera de ese sweet spot es donde los proyectos se complican.

Los cuatro enfoques en detalle

Flutter

Flutter ha ganado terreno serio en enterprise y se ha consolidado como nuestra opción preferida para la mayoría de proyectos nuevos. El rendimiento es excelente, el ecosistema de packages ha madurado, y Dart es un lenguaje más productivo de lo que la gente espera.

Por qué nos gusta:

  • Rendimiento near-native real. Flutter compila a código nativo ARM. No hay bridge, no hay JavaScript runtime intermediario. El rendering engine (Impeller en 2026) es propio, lo que significa que Flutter controla cada pixel en pantalla.
  • UI pixel-perfect en ambas plataformas. Lo que diseñas es lo que sale. No hay sorpresas de "en Android el botón se ve diferente". Esto ahorra decenas de horas de QA por release.
  • Hot reload que realmente funciona. No es solo "recarga la pantalla". Mantiene el estado de la app mientras inyecta los cambios. Puedes estar en la pantalla 5 de un flujo de checkout, cambiar un estilo, y ver el resultado sin volver a navegar.
  • Dart es subestimado. Type safety, null safety desde la versión 2.12, async/await nativo, y una curva de aprendizaje suave para devs que vienen de TypeScript o Java. El pattern matching y los sealed classes que llegaron en Dart 3 son excelentes para modelar estados de UI.

Dónde duele:

  • Tamaño del binario. Una app Flutter vacía pesa ~15MB. Con assets, packages y el engine, fácilmente llegas a 30-40MB. Para mercados donde el tamaño importa (economías emergentes, usuarios con almacenamiento limitado), esto es un factor.
  • Integración con APIs nativas. Cuando necesitas algo que no tiene package (un SDK específico de hardware, por ejemplo), escribir platform channels es tedioso. Ha mejorado con Pigeon y FFI, pero sigue siendo más trabajo que hacerlo en nativo.
  • Ecosistema más pequeño que JavaScript. Aunque ha crecido enormemente, no se compara con npm. Para integraciones poco comunes, a veces no hay package y toca escribir el binding.
// Flutter en 2026: Material 3, Riverpod, go_router
// El ecosistema está maduro y estable
class EventsScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final events = ref.watch(eventsProvider);
    return events.when(
      data: (list) => EventGrid(events: list),
      loading: () => const ShimmerGrid(),
      error: (e, _) => ErrorView(message: e.toString()),
    );
  }
}
// Patrón que usamos para manejar estados complejos con Riverpod + Freezed
@freezed
class EventState with _$EventState {
  const factory EventState.initial() = _Initial;
  const factory EventState.loading() = _Loading;
  const factory EventState.loaded({
    required List<Event> events,
    required EventFilters filters,
    @Default(false) bool hasMore,
  }) = _Loaded;
  const factory EventState.error(String message) = _Error;
}

@riverpod
class EventsNotifier extends _$EventsNotifier {
  @override
  EventState build() => const EventState.initial();

  Future<void> loadEvents({EventFilters? filters}) async {
    state = const EventState.loading();
    try {
      final events = await ref.read(eventRepositoryProvider).getEvents(
        filters: filters ?? EventFilters.defaults(),
      );
      state = EventState.loaded(
        events: events.items,
        filters: filters ?? EventFilters.defaults(),
        hasMore: events.hasNextPage,
      );
    } catch (e) {
      state = EventState.error(e.toString());
    }
  }
}

Nuestro track record con Flutter: 4 apps en producción, incluyendo Nudato mobile. La más compleja tiene 60+ pantallas, push notifications, pagos in-app, offline-first con sincronización, y cámara para escaneo de QR. Ningún problema de rendimiento en producción.

React Native

Sigue siendo la opción más popular gracias al ecosistema JavaScript y la New Architecture (Fabric + TurboModules) que finalmente llegó a producción. Si tu equipo vive en el mundo JavaScript, React Native es una opción muy competitiva.

Por qué funciona:

  • Ecosistema JavaScript masivo. La mayoría de tu lógica de negocio (validaciones, transformaciones de datos, state management) puede compartirse con tu web si también es React. Hemos tenido proyectos donde el 40% del código se comparte entre web y mobile.
  • New Architecture (Fabric + TurboModules). Elimina el bridge asíncrono que era el cuello de botella histórico de React Native. La comunicación entre JS y nativo ahora es síncrona y mucho más rápida. Las listas complejas que antes lagged ya funcionan bien.
  • Expo lo simplifica todo. Expo en 2026 es absurdamente bueno. EAS Build para compilar en la nube, Expo Router para navegación file-based, push notifications configuradas en 5 minutos, OTA updates sin pasar por App Store review. Para el 80% de las apps, nunca necesitas tocar Xcode o Android Studio.
  • Community y hiring. Encontrar devs React Native es significativamente más fácil que encontrar devs Flutter o nativos iOS/Android. Para startups que necesitan escalar equipo rápido, esto importa.

Dónde duele:

  • Performance en animaciones complejas. Aunque la New Architecture mejoró mucho, las animaciones que requieren sincronización frame-by-frame entre JS y UI thread siguen siendo más complejas que en Flutter o nativo. Reanimated 3 ayuda, pero la mental model es más compleja.
  • Debugging "the bridge" legacy. Si usas libraries que todavía no migraron a la New Architecture, puedes encontrar bugs sutiles de serialización. Cada vez son menos, pero existen.
  • Fragmentación del ecosistema. Hay 5 formas de hacer navegación, 4 de hacer state management, 3 de hacer formularios. Para equipos sin experiencia, elegir el stack correcto dentro de React Native es un proyecto en sí.
// React Native con Expo Router + TanStack Query
// Nuestro stack actual para nuevos proyectos RN
// app/(tabs)/events.tsx
import { useQuery } from '@tanstack/react-query';
import { FlashList } from '@shopify/flash-list';
import { EventCard } from '~/components/EventCard';
import { eventApi } from '~/api/events';

export default function EventsScreen() {
  const { data, isLoading, error, refetch } = useQuery({
    queryKey: ['events'],
    queryFn: eventApi.getUpcoming,
    staleTime: 5 * 60 * 1000,
  });

  if (isLoading) return <EventsSkeleton />;
  if (error) return <ErrorView onRetry={refetch} />;

  return (
    <FlashList
      data={data?.events}
      renderItem={({ item }) => <EventCard event={item} />}
      estimatedItemSize={120}
      onRefresh={refetch}
      refreshing={isLoading}
    />
  );
}

Nuestro track record con React Native: 5 apps en producción. La más exitosa es una app de delivery B2B con 15K usuarios activos diarios, tracking GPS en tiempo real, y chat integrado. Funciona sin problemas.

Nativo puro (Swift / Kotlin)

Siempre será la mejor opción en rendimiento absoluto y acceso a APIs del sistema. Pero el coste de mantener dos codebases es real, y en 2026, los casos donde realmente necesitas nativo puro son cada vez menos.

Cuándo tiene sentido de verdad:

  • Apps con uso intensivo de hardware. Cámara con procesamiento de imagen en tiempo real (AR, escaneo de documentos con ML), sensores avanzados (giroscopio, acelerómetro para fitness tracking preciso), Bluetooth Low Energy para IoT.
  • UX que necesita sentirse 100% plataforma-nativa. Si tu app es una extensión del sistema operativo (un launcher, un teclado, un widget de sistema), nativo es el único camino.
  • Equipos con especialistas iOS/Android dedicados. Si ya tienes un equipo de 3 devs iOS y 3 devs Android, cambiar a multiplataforma no necesariamente es una mejora.
  • SDKs de terceros que solo tienen bindings nativos. Algunos SDKs de pago, identidad biométrica, o hardware específico solo ofrecen APIs en Swift/Kotlin.

Cuándo no tiene sentido:

  • MVPs y validación de ideas. Hacer la misma app dos veces es un lujo que la mayoría de startups no se puede permitir.
  • Apps CRUD de contenido, formularios, y listas. No necesitas rendimiento nativo para mostrar una lista de pedidos.
  • Presupuesto limitado. Dos codebases significan dos pipelines de CI/CD, dos conjuntos de tests, dos procesos de release, y dos sets de bugs.
// Swift con SwiftUI — el estado del arte en iOS
// Ejemplo: vista de eventos con async/await
struct EventsView: View {
    @StateObject private var viewModel = EventsViewModel()
    @State private var searchText = ""

    var body: some View {
        NavigationStack {
            List {
                ForEach(viewModel.filteredEvents(searchText)) { event in
                    NavigationLink(value: event) {
                        EventRow(event: event)
                    }
                }
            }
            .searchable(text: $searchText, prompt: "Buscar eventos")
            .navigationTitle("Eventos")
            .refreshable { await viewModel.refresh() }
            .navigationDestination(for: Event.self) { event in
                EventDetailView(event: event)
            }
        }
        .task { await viewModel.loadEvents() }
    }
}

PWA: La cuarta opción que muchos ignoran

Las Progressive Web Apps merecen una mención seria como alternativa. No son "una web con un ícono en el home screen" — en 2026, las PWAs tienen acceso a push notifications, offline storage, cámara, geolocalización, y hasta Bluetooth en Chromium.

Cuándo es la opción correcta:

  • Distribución sin App Store. Si tu modelo de negocio no requiere estar en la App Store (apps B2B internas, herramientas para equipos, plataformas donde los usuarios llegan por link), una PWA elimina el proceso de review y las comisiones del 15-30%.
  • Presupuesto extremadamente limitado. Una sola codebase web que funciona en desktop, tablet y móvil.
  • Actualización instantánea. Sin proceso de review de Apple/Google, sin esperar a que los usuarios actualicen. Deployeas y todos tienen la versión nueva.
  • Mercados con limitaciones de datos. Las PWAs se pueden instalar desde el browser sin descargar 40MB de una App Store.

Cuándo no funciona:

  • Si necesitas push notifications en iOS con alta fiabilidad (las PWA push en iOS siguen siendo limitadas)
  • Si necesitas In-App Purchases (las stores no permiten bypass de su sistema de pagos)
  • Si necesitas acceso a APIs nativas avanzadas (HealthKit, ARKit, NFC en iOS)
  • Si tu público objetivo espera encontrar la app en la App Store
// Service Worker para offline-first en PWA
// sw.ts — Workbox hace que esto sea manejable
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';

// Precache static assets
precacheAndRoute(self.__WB_MANIFEST);

// API calls: stale-while-revalidate
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate({
    cacheName: 'api-cache',
    plugins: [
      new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 3600 }),
    ],
  })
);

// Images: cache-first
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 30 * 24 * 3600 }),
    ],
  })
);

Tabla comparativa detallada

| Criterio | Flutter | React Native | Nativo (Swift/Kotlin) | PWA | |----------|---------|-------------|----------------------|-----| | Rendimiento | Excelente | Muy bueno (New Arch) | El mejor | Bueno | | UI consistency | Pixel-perfect cross-platform | Usa componentes nativos | 100% nativa por definición | Depende del browser | | Tamaño binario base | ~15MB | ~8MB | ~3MB | 0 (web) | | Time to market | Rápido | Rápido | 2x más lento | El más rápido | | Coste de desarrollo | 1x | 1x | 1.8-2x | 0.7x | | Coste de mantenimiento | 1x | 1x | 1.8-2x | 0.5x | | Hot reload | Excelente | Bueno (Fast Refresh) | Xcode Previews / Compose Preview | Instant (browser) | | Acceso a APIs nativas | Bueno (platform channels) | Bueno (TurboModules) | Completo | Limitado | | Ecosistema de packages | Grande y creciendo | Enorme (npm) | Completo (oficial) | Web standards | | Hiring dificultad | Media | Fácil | Difícil (especialistas) | Fácil (web devs) | | Offline support | Excelente | Bueno | Excelente | Bueno (Service Workers) | | Push notifications | Excelente | Excelente | Excelente | Limitado en iOS | | App Store required | Si | Si | Si | No |

Rendimiento: Los números reales

La conversación de "rendimiento" en mobile suele ser abstracta. Estos son los números que hemos medido en producción en apps comparables (lista scrolleable con imágenes, 500+ items):

Tiempo de inicio (cold start, dispositivo mid-range):

  • Nativo iOS (Swift): ~400ms
  • Flutter: ~600ms
  • React Native (New Arch): ~750ms
  • React Native (Old Arch): ~1.2s
  • PWA: ~300ms (si está cacheada), ~2s (primera carga)

FPS durante scroll agresivo (lista con imágenes):

  • Nativo: 60fps constante
  • Flutter (Impeller): 58-60fps
  • React Native (FlashList + New Arch): 55-60fps
  • React Native (FlatList + Old Arch): 40-55fps
  • PWA: 55-60fps (depende del browser)

Uso de memoria (app con 10 pantallas navegadas):

  • Nativo: ~80MB
  • Flutter: ~110MB
  • React Native: ~120MB
  • PWA: ~60MB (browser gestiona)

La conclusión: para el 95% de las apps, la diferencia de rendimiento entre Flutter, React Native (New Arch) y nativo es imperceptible para el usuario. Los frames dropeados y la latencia que los usuarios notan casi siempre son problemas de código (queries lentas, imágenes sin optimizar, re-renders innecesarios), no del framework.

Caso real: App de gestión para cadena de restaurantes

Un cliente con una cadena de 25 restaurantes necesitaba una app para sus managers: visualizar ventas en tiempo real, gestionar turnos del personal, recibir alertas de inventario bajo, y aprobar pedidos a proveedores.

Requisitos clave: funcionar offline (muchos restaurantes tienen WiFi inestable), notificaciones push fiables, cámara para escanear facturas de proveedores, y debía estar en ambas stores (el 60% de los managers usaban Android, 40% iOS).

Nuestra decisión: Flutter. El razonamiento fue:

  1. Offline-first era critico, y Flutter con Drift (SQLite) + un sync engine custom nos daba control total
  2. La UI debía ser idéntica en ambas plataformas (los managers rotaban entre restaurantes con tablets diferentes)
  3. La cámara era para escaneo simple de documentos, no AR — bien cubierto por packages existentes
  4. El equipo era de 2 devs mobile — mantener dos codebases no era viable

Resultado: app en producción en 10 semanas, 25 restaurantes activos, 98.5% crash-free rate, y los managers la prefieren sobre el sistema web anterior porque "va más rápido y funciona sin WiFi".

Lo que hemos aprendido en 12+ apps

  1. Flutter gana en consistencia visual — la misma UI en ambas plataformas sin ajustes, lo que reduce significativamente el tiempo de QA
  2. React Native gana en ecosistema y contratación — si tu equipo ya domina JavaScript, la curva es mínima, y encontrar devs es más fácil
  3. Nativo gana en edge cases — cuando necesitas el último 5% de rendimiento o acceso a APIs del sistema que acaban de salir (siempre hay 3-6 meses de delay en que los packages multiplataforma soporten nuevas APIs)
  4. PWAs son subestimadas — para apps B2B internas sin requisito de App Store, una PWA puede ser la opción más eficiente
  5. Ninguno gana en todo — y eso está bien. La madurez como equipo de ingeniería es aceptar que no hay una bala de plata
  6. La decisión de framework es menos importante que la calidad del código. Hemos visto apps nativas horribles y apps Flutter espectaculares, y viceversa. Un equipo disciplinado con buen testing y code review produce buenas apps en cualquier framework
  7. El mayor riesgo no es técnico, es de producto. La app más optimizada del mundo no sirve si resuelve el problema equivocado. Lanza rápido, mide, y itera

La decisión correcta no es la "tecnología mejor" sino la que te permite lanzar, medir y mejorar dentro de tu presupuesto y timeline.


¿Necesitas una app móvil? Cuéntanos tu proyecto y te recomendamos el enfoque correcto para tu caso.