¿Por qué está ganando Flutter?

El año pasado, he estado escribiendo aplicaciones Flutter para iOS y Android de todos modos. Antes de eso, tenía y tengo 5 años de experiencia con Xamarin. Han sido 5 años maravillosos. Gracias a Xamarin y mi amor por este marco, en principio me mudé al campo de los desarrolladores, esta herramienta me ayudó a ganar mucho dinero, conocimiento y encontrar colegas maravillosos. Entonces, ¿por qué estoy escribiendo en Flutter ahora? Respuesta corta, porque Flutter cubre todas las necesidades del desarrollo multiplataforma.


Un poco de historia


Corríjame si me equivoco, pero 2009 fue clave en muchos aspectos para el desarrollo móvil en general y el desarrollo multiplataforma en particular. En 2009, se lanzó el iPhone 3gs, que le permitió ejecutar aplicaciones de terceros desde la AppStore. Por primera vez, esta oportunidad apareció un año antes en el iPhone 3g, pero 3gs se ha convertido en un iPhone realmente masivo y "popular". Nuevamente, un año antes, en septiembre de 2008, Android se presentó al público y en 2009 muchos fabricantes de teléfonos comenzaron a probar Android para sus nuevos modelos de teléfonos. En la primavera de 2009, Nitobi presentó PhoneGap, un nuevo marco para crear aplicaciones multiplataforma basadas en HTML5, CSS y JS. En el mismo año, en septiembreXimian lanzó MonoTouch, que le permitió escribir aplicaciones iOS usando Mono y C #. En el mismo 2009, en diciembre, Rovio Entertainment lanzó un juego para iOS y, por un momento, Maemo, que en muchos aspectos marcó el comienzo de la industria de los juegos móviles: Angry Birds. El último ejemplo aquí no es accidental.

El primer marco multiplataforma "para las personas" puede considerarse PhoneGap (desarrolladores de Qt, no arrojen piedras). Fue una idea maravillosa y muy obvia: llevar la web al mundo del desarrollo móvil. Para 2009, las capacidades de la web ya habían comenzado a extenderse más allá del navegador ( hello node.js), mientras que escribir aplicaciones web en JS era bastante sencillo. El segundo punto, no menos importante, es la representación de la interfaz de usuario. La forma en que se realiza el renderizado recae en el motor del navegador y todos estos motores siguen más o menos los estándares W3C para HTML, CSS y DOM. Cualquier desarrollador web que haya creado un sitio espera que su sitio se vea casiidéntico en cualquier navegador, en cualquier plataforma. Esto, en mi opinión, es el aspecto más importante de la web como plataforma abierta. ¿Por qué debería aprender un nuevo lenguaje / marco para dibujar la interfaz de usuario para cada una de las plataformas, si durante mucho tiempo existe un estándar para modelar la interfaz de usuario para diferentes navegadores?

Después de eso, Cordova se separó de PhoneGap y de Ionic. Parece que este es un marco ideal, pero hubo 2 puntos: rendimiento e integración del sistema operativo. Uno de los objetivos principales o, si lo desea, los puntos de referencia de la aplicación, escritos en soluciones multiplataforma era su "natividad". Aquellos. Idealmente, el 100% de los usuarios deberían considerar que su aplicación multiplataforma es nativa. Y esto significa que debería verse como nativo, funcionar como nativo y tener toda la integración posible con el sistema operativo. Al principio, todos estos puntos para PhoneGap eran inalcanzables, las capacidades de los teléfonos inteligentes hace 10 años no eran suficientes para la representación de la interfaz de usuario de 60 fps, la integración con el sistema operativo era mínima. Ahora hay bastantes aplicaciones en Ionic que son difíciles de distinguir de las nativas, pero imitar una aplicación nativa sigue siendo una tarea.y no dado como tal. Resumamos un poco. Escribir aplicaciones web, o más bien aplicaciones híbridas en iOS y Android, es posible y conveniente. Es conveniente porque el mecanismo de representación de la interfaz de usuario se encuentra completamente en la plataforma WebView, además hay una capa de programadores ya capacitados que están bien versados ​​en la web.Sin embargo, en aplicaciones híbridas, el rendimiento y la integración del sistema operativo pueden ser poco convincentes.

Al mismo tiempo que PhoneGap, MonoTouch se lanzó en 2009, que luego pasó a llamarse Xamarin.iOS. Además, en el mismo año, se lanzó Titanium, que a su vez también permitió escribir aplicaciones iOS en javascript. Al principio, Titanium trabajó exactamente en el mismo paradigma que PhoneGap: confiaba en WebView. Pero luego adoptaron el enfoque de Xamarin. ¿Cuál es este enfoque? Se puede ver como algo en el medio. El enfoque de Xamarin / Titanium / React.Native es que, en lugar de intentar crear / migrar su renderización de UI existente, el marco simplemente se integra con el nativo existente.

En lugar de dibujar un formulario en HTML, Xamarin llama a un elemento UI nativo para esto (UITextField, TextEdit, etc.). De hecho, ¿por qué reinventar la rueda? Todos los elementos de IU necesarios existen en SDK nativos y tiempos de ejecución, solo necesita aprender a comunicarse con ellos desde sus máquinas virtuales (mono, v8, etc.). Al mismo tiempo, como ya lo adivinó, puede usar su C #, JS, TS, F #, Kotlin, etc. favoritos, y al mismo tiempo el código que no interactúa directamente con la interfaz de usuario es 100% multiplataforma. Puedes ir aún más lejos. El mismo UITextField y TextEdit son entidades conceptualmente idénticas, tienen bastantes propiedades e interfaces de interacción similares, y por lo tanto, puede crear una entrada abstracta (hola Xamarin.Forms) y trabajar solo con ella, por raro ( no muy)) excepción al elemento de la interfaz de usuario de la plataforma. No menciono que si su VM puede funcionar con la interfaz de usuario de forma nativa, lo más probable es que su VM pueda llamar a cualquier API de plataforma. Esta parece ser la opción perfecta. Interfaz de usuario nativa, rendimiento nativo (hola puentes en React.Native), integración 100% del sistema operativo. ¿Es esto realmente perfecto? Lo más probable es que no, y el problema es que en realidad estas soluciones no resuelven el problema del desarrollo multiplataforma: una sola interfaz de usuario. La disfrazan. Quiero escribir una vez, correr a todas partes. Esto está lejos de ser el mejor lema para todo tipo de programas y problemas, pero encaja bien con la interfaz de usuario. Quiero escribir la interfaz de usuario igual para todos, independientemente de la plataforma. ¿Por qué un desarrollador web puede permitirse usar HTML y CSS para escribir un sitio que luego se mostrará de la misma manera en Safari en iOS y Chrome en Android, pero no en un desarrollador nativo?

De hecho, los programadores han escrito durante mucho tiempo una interfaz de usuario de alto rendimiento con una base de código común para iOS y Android. Estos programadores se llaman desarrolladores de juegos. Angry Birds fue escrito en el motor Cocos2d-x, Cuphead en Unity y Fortnite en Unreal Engine. Si los motores del juego pueden mostrar escenas impresionantes en su teléfono, entonces los botones y las listas con animación fluida definitivamente podrán hacerlo. Entonces, ¿por qué nadie los usa en esta línea? La respuesta es simple y banal, no están destinados a esto. Cuando abres el juego, depende absolutamente de la linterna cuánto se parece la IU a una nativa, casi nunca necesitas interactuar con geolocalización, botones, una cámara de video, etc. Mientras juegas, vives una vida diferente en tu pequeño mundo que se representa a través de Canvas en tu UIViewController / Activity. por lo tantolos motores de juego tienen una integración relativamente pobre con el sistema operativo , por lo que no hay (o no he visto) que imite el motor superior de la interfaz de usuario nativa.

Subtotales


Para un marco multiplataforma ideal, necesitamos:

  • Mapeo de IU nativo
  • Rendimiento nativo de la interfaz de usuario
  • 100% de capacidad para llamar a cualquier API del sistema operativo, como si fuera una aplicación nativa

Ahora piensa que comenzaré a fallar bajo Flutter, pero ya escucho comentarios enojados: “¿Dónde está Qt? ¡Él puede hacer todo esto! De hecho, Qt en un grado u otro se ajusta a estos criterios. Aunque dudo mucho del primero de ellos. Pero el problema principal de Qt no es la dificultad de escribir una IU nativa, el problema principal es C ++. Entonces ya me estoy limpiando la cara del asador de codificadores de mano de obra en las ventajas. Pros es un cuchillo suizo con esteroides anabólicos, con los profesionales puedes hacer todo. Pero yo, como desarrollador frontend, no necesito todo esto. Necesito un lenguaje simple y comprensible que funcione con IU y E / S. Entonces, a nuestros tres puntos anteriores se agregó:

  • Lenguaje fácil de aprender y bastante expresivo.
  • Rantime que encaja bien en el paradigma de desarrollo frontend

Bueno, ahora que hemos destacado algunas métricas de una buena herramienta multiplataforma para desarrollar aplicaciones móviles, podemos revisar cada una de ellas y ver cómo se implementa en Flutter.

Mapeo de IU nativo



Como descubrimos anteriormente, hay dos enfoques opuestos para trabajar con UI en marcos multiplataforma. Este es un render de UI usando WebView o llamadas a elementos nativos de UI en cada plataforma. Cada enfoque tiene ventajas y desventajas. Pero no cubren la gama completa de necesidades del desarrollador: se ven indistinguibles del rendimiento nativo de UI + nativo. Flutter cubre todas estas necesidades con una cabeza. El equipo de Flutter gastó una cierta cantidad de recursos en la creación de elementos "nativos" en el propio marco. Todos los widgets en Flutter se dividen en tres grandes categorías:


Si va a la sección de Cupertino, verá que estos widgets son indistinguibles de los elementos nativos de iOS. Como desarrollador que usa Flutter por un tiempo, puedo confirmar que no se pueden distinguir. Si usa CupertinoDatePicker, por ejemplo, al desplazarse sentirá exactamente lo mismo, una buena respuesta del motor Taptic / Haptic en su iPhone como si fuera un elemento nativo de la aplicación nativa. Diré más, periódicamente abro la aplicación del sitio realtor.com en mi iPhone y hasta hace poco no tenía idea de que estaba escrito en Flutter (o en algo no nativo).

Flutter no solo te permite usar widgets "nativos" para 2 plataformas, sino también crear el tuyo propio, ¡y es muy fácil! Todo el paradigma es que todo funciona con widgets. Puede crear elementos de UI y animaciones increíblemente complejos en poco tiempo. Los encantos y la sabiduría del enfoque para trabajar con la interfaz de usuario en Flutter se han descrito recientemente en este artículo sobre Habr, recomiendo leer. Porque todo esto funciona en un único motor de gráficos que procesa directamente todo esto para cada plataforma (hablaremos de eso más adelante), puede estar seguro de que todo se mostrará como lo planeó.

Otro punto bastante sorprendente. Flutter admite plataformas que comienzan con iOS 8 y Android API v16. Desde una perspectiva de representación de la interfaz de usuario, Flutter realmente no importa qué API están disponibles en una plataforma en particular. Tendría la oportunidad de trabajar con Canvas y algún tipo de interacción con el subsistema de gráficos. Y esto significa que podemos dibujar los últimos elementos de la interfaz de usuario de AndroidX, por ejemplo, en un teléfono de 8 años. Ciertamente hay una pregunta sobre el rendimiento de este enfoque en las plataformas compatibles más antiguas, pero esta es otra pregunta.

Rendimiento nativo de la interfaz de usuario



Como puede ver, el enfoque de Flutter para la representación de la interfaz de usuario es más cercano al de las aplicaciones híbridas como Ionic. Tenemos un solo motor para renderizar la interfaz de usuario en todas las plataformas, esta es la Biblioteca de gráficos Skia . Google compró Skia como producto en 2005 y lo convirtió en un proyecto de código abierto. Esto al menos sugiere que este es un producto bastante maduro. Algunas características de rendimiento de Skia:

  • Copia en escritura para elementos gráficos y otros tipos de datos
  • Usar memoria de pila siempre que sea posible para reducir la fragmentación
  • Seguridad de roscas, para una mejor paralelización

No he encontrado pruebas de rendimiento convincentes de Skia en comparación con bibliotecas similares (ver El Cairo ), pero algunas pruebas muestran un aumento del rendimiento del 50% en promedio, excepto en algunas situaciones específicas. Sí, esto no es particularmente importante, porque estas pruebas se basan en el uso de OpenGL en equipos de escritorio y ...

Skia puede interactuar con muchos backends de GPU. Desde recientetiempo en iOS, desde la versión 11, Flutter usa Metal como GPU de backend por defecto. En Android, comenzando con API 24 - Vulkan. Para las siguientes versiones: OpenGL. Todo esto nos da una ganancia obvia en productividad. En otras plataformas de "hardware", según tengo entendido, Skia / Flutter usa OpenGL, que en principio no nos impide escribir aplicaciones con suficiente rendimiento gráfico.

La web se destaca. Por el momento, todo el procesamiento de la interfaz de usuario todavía se encuentra en el paquete Canvas / HTML. Por lo tanto, Skia no está involucrado de ninguna manera en este proceso. Además, la máquina virtual Dart no interactúa directamente con el DOM. Primero viene la conversión a js. Todo esto no tiene el mejor efecto sobre la productividad y es directamente perceptible a simple vista. Sin embargo, se está trabajando para implementar CanvasKiten Flutter, que a su vez permitirá que Skia se use en navegadores a través de WebGL.

Finalmente, los programadores de C # han estado usando SkiaSharp durante un tiempo relativamente largo, un envoltorio sobre Skia para Mono / .Net x. Y la comunidad Xamarin usa esta biblioteca para dibujar elementos de IU personalizados y esta es una biblioteca muy popular. Si esto no es una victoria, entonces no sé qué es.

100% de capacidad para llamar a cualquier API OS


En Flutter hay 2 principios de interacción con el mundo "exterior":


Los canales de plataforma le permiten interactuar con el tiempo de ejecución / API nativo a través de un sistema de mensajería. Desde un punto de vista arquitectónico, esto se puede ver de la siguiente manera. Visualmente, Flutter es solo un Canvas, que se extiende a pantalla completa en el único Activity / UIViewController de su aplicación nativa. Este es exactamente el mismo enfoque que utilizo los desarrolladores de juegos (motores de juegos). Aquellos. Puede abrir el proyecto iOS / Android de su aplicación y agregar cualquier otra funcionalidad a Swift / Kotlin / etc. El problema es que el tiempo de ejecución nativo y Dart VM no sabrán nada el uno del otro (además del hecho de que el tiempo de ejecución nativo sabrá que la aplicación tiene Canvas y se muestra algo allí). Además, si, por ejemplo, abre el archivo MainActivity.kt de su proyecto de Android, verá algo como esto:

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
  }
}

¿Has notado que tu actividad hereda de FlutterActivity? Esto nos da la oportunidad de configurar el mecanismo para enviar mensajes directamente a Flutter / DartVM. Para hacer esto, necesitamos anular el método configureFlutterEnginey determinará el nombre del método llamado y el nombre del canal para enviar mensajes asincrónicos. Todas. ¡Esto hace posible escribirnos cualquier código nativo y llamar a cualquier API nativa! Al mismo tiempo, ya hay una gran cantidad de complementos (paquetes) que le evitan escribir código nativo, solo puede usar Dart. ¡Esto es simplemente maravilloso! Escribe la interfaz de usuario por separado y una vez para cualquier plataforma, usa DartVM para trabajar con la interfaz de usuario, E / S y solo como un componente informático, usa complementos que implementan características nativas y que cubren el 99% de toda la funcionalidad. Y si esto no es suficiente, usted escribe de forma nativa y se comunica a través del mecanismo del mensaje. Historia.

El segundo mecanismo es una interfaz de función externa o FFI. Este es un término bastante común para el mecanismo de iterope con otros idiomas, principalmente C. En el mundo .Net, este mecanismo se llama P / Invoke, para JVM es JNI. En resumen, esta es la capacidad de interactuar con bibliotecas escritas en C / C ++ / etc. En el momento de .Net Framework, por ejemplo, no había software escrito en C # y la gran mayoría del software estaba escrito en C / C ++, por lo que se necesitaba un mecanismo para trabajar con estas bibliotecas. Lo mismo se aplica a JVM, Python, lo que sea. FFI se utiliza de una forma u otra en todos los marcos móviles multiplataforma. Más recientemente, DartVM también ha comenzado a admitir FFI para la interoperación con C y JavaScript. Si bien esta característica está en una rama beta, pero ya está disponible para su uso (bajo su propio riesgo y riesgo).

Como puede ver, Flutter y DartVM cubren el 100% de las posibilidades en plataformas nativas, y aún más.

Lenguaje fácil de aprender y bastante expresivo.


Lo admito honestamente, mientras que Dart para mí sigue sin ser el mejor idioma del mundo. No hay un sistema de tipo estricto, no hay bollos funcionales, como las características de coincidencia de patrones o inmutabilidad (como se entregarán pronto), etc. Sobre el sistema de tipos, Dart fue originalmente concebido como un lenguaje "sin un típico", ala JS, pero para el soporte normal para la compilación de AOT, sin embargo, fue necesario llevar el sistema de tipos a un nivel más estricto, aunque no completamente, diría yo. Todavía me molesta trabajar con firmas de métodos, es decir, con argumentos. Todos estos corchetes, @requiredpor alguna razón , son enfurecedores . Pero el dardo es un lenguaje muy fácil de aprender. En sintaxis, este es un cruce entre Java y JS para mí. Dart perdona mucho, como JS. En general, este es un lenguaje bastante fácil de aprender, no he experimentado ningún problema significativo.

Rantime que encaja bien en el paradigma de desarrollo frontend


Ahora hablemos de Dart VM. En general, Dart VM incluye muchas cosas, desde GC hasta Profiler y Observatory. Aquí solo quiero hablar sobre GC y el tiempo de ejecución condicional. Puede familiarizarse con cómo funciona el tiempo de ejecución y en qué consiste aquí . No soy un experto en este campo, pero para mí, noté algunas de las ventajas de Dart VM, que trataré de describir. Antes de esto, me gustaría señalar que Dart y la máquina virtual correspondiente se desarrollaron inicialmente como un reemplazo para JS, que, por así decirlo, insinúa el enfoque en el desarrollo frontend.

Aislamientos

Dart VM tiene el concepto Isolate. Aislar es una combinación de un subproceso principal que se ejecuta directamente en el código Dart y el montón aislado, donde los objetos del código Dart se asignan realmente. Esta es una estructura simplificada. Isolate también tiene hilos auxiliares / del sistema, hay hilos del sistema operativo que pueden entrar y salir de Isolate, etc. La pila también está presente en Isolate pero usted, como usuario, no opera en ella. Lo principal que debe enfatizarse aquí es que si observa un Isolate, entonces este es un entorno de subproceso único. Por defecto, Flutter usa un aislamiento predeterminado. ¿No se parece a nada? Sí, este es el entorno JS. Al igual que en JS, los programadores de Dart no pueden trabajar con subprocesos múltiples. Alguien podría pensar que esto es un desastre, simplificación e infracción de los derechos de los desarrolladores reales, pero creo que cuando se trabaja con UI,cuando opera con un DOM condicional (y no dibuja polígonos en la pantalla), no necesita hacerlo, es peligroso operar con varios hilos.

Aquí, por supuesto, era astuto, si realmente quieres, puedes usar el Isolate lanzado por separado para realizar tareas paralelas (hola WebWorkers) Aquí puedes ver en detalle cómo puedes trabajar con Isolate adicional en Flutter. En general, los aislados, como su nombre lo indica, no se conocen entre sí, no mantienen enlaces entre sí y se comunican a través de un sistema de mensajes.

Además del enfoque de subproceso único, el hecho de que se asigne un montón independiente para cada aislamiento sin la capacidad de manipular la pila de este subproceso es, en mi opinión, un enfoque muy bueno. Si está escribiendo una aplicación de servidor que manipula una gran cantidad de líneas, por ejemplo, y estas líneas se almacenan en un montón, donde aparecen y desaparecen a una velocidad tremenda, mientras fragmentan la memoria y agregan trabajos de GC, cualquier forma de transferir estas líneas, o al menos en parte, desde los montones en la pila ahorrarán recursos y mejorarán el rendimiento. Un ejemplo es regular, pero me entiendes. Pero cuando se trabaja con UI, donde existe, posiblemente, una cantidad suficiente de elementos de UI que pueden tener una vida útil corta (por ejemplo, animación), pero al mismo tiempo solo un cliente y la cantidad de datos procesados ​​es insignificante en comparación con la aplicación del servidor,La capacidad de trabajar directamente con la pila es simplemente innecesaria. No estoy hablando de boxing / unboxing, que podría ser en este caso y que es absolutamente inútil. Y debe tenerse en cuenta que los objetos en Dart VM se asignan con bastante frecuencia. Incluso para generar la cantidad doble del método Dart, la VM asigna por separado una pieza en el montón. ¿Cómo maneja el GC esta carga? Echemos un vistazo.

Young Space Scavenger (y Parallel Mark Sweep)

Primero, como todos los GC, el GC en Dart VM tiene generaciones. Además, el GC en Dart VM se puede dividir según el principio del trabajo en 2 componentes: Young Space Scavenger y Parallel Mark Sweep. No me detendré en el último principio, este es un principio bastante popular de limpieza de memoria, que se implementa en casi todas partes y no le da a Flutter una ventaja especial. Estamos interesados ​​en el primero. El principio de funcionamiento de Young Space Scavenger está bien ilustrado en la siguiente imagen:


Demuestra claramente las ventajas de este enfoque. Young Space Scavenger funciona para los objetos más nuevos en la memoria, podemos decir que para la primera / cero generación de objetos. A menudo, y esto es característico de la máquina virtual Flutter / Dart, la mayoría de los objetos nuevos tienen una vida corta. En una situación en la que asigna muchos objetos que no duran mucho, la memoria puede estar muy fragmentada. En este caso, tendrá que pagar memoria o tiempo de procesador para solucionar el problema (aunque no debe solucionar el problema con dichos métodos). Young Space Scavenger resuelve este problema. Si mira la imagen de arriba, entonces realmente no hay 6 pasos, no necesita borrar el primer fragmento de memoria, por defecto creemos que este fragmento está vacío después de copiar objetos en el segundo. Bueno, al copiar objetos supervivientes en el segundo fragmento,naturalmente los configuramos uno por uno sin crear fragmentación. Todo esto permite que VM asigne muchos objetos nuevos a un precio bastante bajo.

Idle Time GC

Como comprende, los equipos de Flutter y Dart VM trabajan en estrecha colaboración y el resultado de esta cooperación puede considerarse el Idle Time GC. Como su nombre lo indica, esta es la recolección de basura en el momento en que no sucede nada. En el contexto de Flutter, en el momento en que la aplicación visualmente no cambia nada. No hay animación, desplazamiento o interacción del usuario. En estos momentos, Flutter envía mensajes a la máquina virtual Dart que ahora, en principio, es un buen momento para comenzar la recolección de basura. Luego, el recolector de basura decide si debe comenzar su trabajo. Por supuesto, la recolección de basura en este sentido se produce para objetos más antiguos que se gestionan a través del barrido de marcas paralelas, que en sí mismo es un proceso bastante costoso y Idle Time GC es un mecanismo muy útil a este respecto.

Hay otras cosas comoCompactación deslizante y punteros comprimidos . El primero es el mecanismo de desfragmentación de la memoria después de ejecutar Parallel Mark Sweep. Este también es un proceso costoso y solo funciona si hay tiempo de inactividad. El segundo enfoque, Compressed Pointers, comprime los punteros de 64 bits en 32 bits, lo que ahorra memoria (creo que esto es mucho más útil en un entorno de servidor que en uno móvil).

Resumen


Si lees hasta esta línea, entonces, en primer lugar, felicidades, y en segundo lugar, tengo que decir que no tengo experiencia escribiendo artículos, por lo que no entiendo si logré comunicar mi punto de vista. Y la idea es simple, cuando escribes una aplicación móvil con Flutter, resulta nativa. Y en forma de bonificación, obtienes una velocidad de desarrollo de aplicaciones muy decente. Hot Reload / Restart es simplemente una cosa indispensable en el desarrollo de Frontend. ¿Te imaginas a alguien que necesite escribir / compilar todo el proyecto para cada navegador, por ejemplo, con cada cambio de color de un botón? Por supuesto no. En general, Hot Reload / Restart merece un artículo separado. Pero estaba distraído.

Mi experiencia con Flutter me dice que este marco será dominante en el futuro cercano. Periódicamente, paso por entrevistas para un puesto de desarrollador de Flutter y, en la mitad de los casos, las empresas que buscan un desarrollador de Flutter en realidad tienen un equipo de desarrolladores nativos móviles. Simplemente probaron Flutter en proyectos interiores / secundarios, quedaron satisfechos / encantados y se mudaron lentamente a Flutter. Esta es una verdadera victoria, me parece. Lo que no se puede decir sobre Xamarin, desafortunadamente. Muy a menudo, la decisión de elegir Xamarin se debe simplemente al hecho de que el resto de la pila está escrito en .Net, que es una pendiente resbaladiza.

Para resumir, quiero decir que si está pensando en qué lado abordar al desarrollar su nueva aplicación móvil, mire Flutter.

All Articles