Funciones de lenguaje. Herramientas de productividad. Integración más completa y amplia con plataformas.
Hoy, en Google I/O, anunciamos la nueva versión 2.17 del SDK de Dart. Esta versión se basa en nuestros temas principales: liderazgo en productividad y portabilidad de plataformas. Ofrece nuevas funciones de lenguaje: enumeraciones con compatibilidad de miembros, un traspaso mejorado de parámetros a superclases y mayor flexibilidad para los parámetros con nombre. Mejoramos las herramientas con una nueva versión principal de package:lints
(la compatibilidad de nuestra herramienta para comparar el código de Dart con nuestras prácticas recomendadas) y una actualización completa de la documentación de las API de biblioteca principales con abundantes muestras de código. A fin de mejorar la integración con plataformas, contamos con nuevas plantillas para usar dart:ffi
(interoperabilidad con el código C nativo) en los complementos de Flutter y compatibilidad experimental con procesadores RISC-V y con la firma de ejecutables de macOS y Windows.
Nuevas funciones de lenguaje para aumentar la productividad
Con el fin de ayudarte a aumentar la productividad, desarrollamos el lenguaje Dart constantemente. Para ello, agregamos nuevas funciones y mejoramos las existentes. Dart 2.17 incorpora mayor compatibilidad con los miembros de enumeraciones, define mejor el uso de los argumentos con nombre en los constructores y hace que el código para traspasar parámetros a superclases no sea tan detallado y repetitivo.
Mejores enumeraciones con miembros
Las enumeraciones sirven para representar un conjunto discreto de estados. Por ejemplo, podemos crear el siguiente modelo para el agua: enum Water { frozen, lukewarm, boiling }
. Ahora bien, supongamos que quisiéramos agregar métodos a enum
, por ejemplo, para convertir cada uno de los estados en una temperatura y admitir que enum
se transforme en una String
. ¿Qué ocurriría en ese caso? Tal vez podríamos usar métodos de extensión para agregar un método waterToTemp()
, pero tendríamos que asegurarnos de que se mantenga en sincronía con enum
. En el caso de la conversión a String
, preferiríamos anular toString()
, pero aún no se admite esta operación.
Dart 2.17 ofrece una compatibilidad general con los miembros de enumeraciones. Esto significa que podemos agregar campos que contengan un estado, constructores que establezcan ese estado y métodos con funcionalidad, además de anular a miembros existentes. Muchos de ustedes venían solicitando esta función; era el tercer problema más votado en el registro de lenguaje.
En nuestro ejemplo del agua, podemos agregar un campo de int
que incluya la temperatura y un constructor predeterminado que tome un valor de int
:
enum Water {
…
final int tempInFahrenheit;
const Water(this.tempInFahrenheit);
}
Para garantizar que se llame al constructor al crear la enum
, debemos invocarlo en cada valor de enum
:
enum Water {
frozen(32),
lukewarm(100),
boiling(212);
…
}
Para admitir la conversión a String
, simplemente debemos anular toString
, que las enums
heredan de Object
:
@override
String toString() => "The $name water is $tempInFahrenheit F.";
De este modo, se puede crear fácilmente una instancia completa de enum
y se pueden invocar métodos en:
void main() {
print(Water.frozen); // Prints “The frozen water is 32 F.”
}
Debajo, encontrarás un ejemplo completo de estos dos enfoques; consideramos que la nueva versión Dart 2.17 es mucho más fácil de leer y mantener.
Superinicializadores
En la jerarquía de herencia de clases, pasar algunos parámetros del constructor al constructor de la superclase es un patrón común. Para hacerlo, la subclase necesita: 1) enumerar cada parámetro en su propio constructor y, luego, 2) invocar al superconstructor con dichos parámetros. Eso conduce al código estándar, que tiene muchas repeticiones, y hace que resulte más difícil leerlo y más molesto mantenerlo.
Varios miembros de la comunidad de Dart contribuyeron a que esto suceda. El usuario de GitHub @roy-sianez presentó este problema de lenguaje hace aproximadamente seis meses y su sugerencia fue similar a la que ya había hecho @apps-transround, otro usuario de GitHub: que la posible solución sería incorporar un constructor nuevo para expresar que se especificó un parámetro en la superclase. Nos pareció una idea excelente, así que la implementamos en Dart 2.17. Como puedes ver en el siguiente ejemplo, esta solución resulta especialmente importante para el código del widget de Flutter. De hecho, cuando aplicamos la nueva función al framework de Flutter, observamos una reducción total de casi 2,000 líneas de código.
Argumentos con nombre en todas partes
Por último, definimos mejor el funcionamiento de los argumentos con nombre cuando se invoca un método. Anteriormente, este tipo de argumentos debía aparecer en último lugar en la lista del método. Esto resultaba molesto en aquellos casos en que era preferible ubicar un argumento posicional al final para facilitar la lectura del código. Por ejemplo, veamos la siguiente invocación del constructor List<T>.generate
: antes, el argumento "growable" debía ubicarse al final, lo cual ocasionaba que el gran argumento posicional que contenía el mismo generador quedara perdido debajo. Ahora, puedes ordenarlos como prefieras: puedes ubicar los pequeños argumentos con nombre al principio y el generador al final.
Para ver más ejemplos de estas tres funciones en acción, consulta nuestras muestras actualizadas de enumeraciones, superinicializadores y parámetros con nombre.
Herramientas de productividad
Continuando con el tema de la productividad, hay varias mejoras en nuestras herramientas principales.
En Dart 2.14, presentamos package:lints
, que funciona con el analizador de Dart para ayudarte a escribir código de Dart que evita errores y usa un estilo canónico, lo cual permite revisiones de código más eficaces. Desde entonces, hay una serie de herramientas lint nuevas en el analizador, que clasificamos cuidadosamente. Entre ellas, seleccionamos diez herramientas lint nuevas para todo el código de Dart y dos herramientas lint nuevas específicas para el código de Flutter. Estas herramientas lint incluyen aquellas que garantizan que las importaciones se incluyan en el archivo pubspec, impiden el uso inadecuado de las comprobaciones de valores nulos en los parámetros de tipo y garantizan un estilo coherente de las propiedades secundarias. Puedes actualizar a las nuevas herramientas lint con un simple comando:
- Para paquetes de Dart:
dart pub upgrade —-major-versions lints
- Para paquetes de Flutter:
flutter pub upgrade —-major-versions flutter_lints
La conexión segura suele utilizarse para habilitar los sockets del TCP protegidos con TLS y SSL. Antes de Dart 2.17, la depuración de estos sockets durante el desarrollo era complicada, ya que no había forma de inspeccionar el tráfico seguro de datos. Ahora se admite la especificación de un archivo keyLog
. Al especificarla, se agrega al archivo una línea de texto en el formato de registro de claves NSS en el momento en que se intercambian nuevas claves de TLS con el servidor. De esta forma, las herramientas de análisis del tráfico de red (como Wireshark) pueden desencriptar el contenido enviado a través del socket. Para obtener información detallada, consulta los documentos de la API de SecureSocket.connect()
.
La documentación de la API que genera la herramienta dart doc
es un recurso fundamental para la mayoría de los desarrolladores de Dart que están aprendiendo a usar nuevas API. A pesar de que nuestras principales API de biblioteca siempre tuvieron descripciones textuales enriquecidas, muchos desarrolladores nos comentaron que prefieren leer el código de muestra que usa las API para aprender a usarlas. En Dart 2.17, optimizamos todas las bibliotecas principales, que ahora incluyen código de muestra completo. Para ello, agregamos código de muestra completo a las 200 páginas más vistas. Por ejemplo, compara la documentación de dart:convert en Dart 2.16 con la página actualizada de Dart 2.17. Esperamos que, de esta forma, resulte mucho más sencillo consultar la documentación.
Logramos incrementar la productividad no solo cuando agregamos nuevas funciones a nuestra plataforma, sino también cuando hacemos una limpieza y quitamos aquellas que ya no se usan. Esto contribuye a que el área de superficie siga siendo pequeña, lo que resulta especialmente importante para los desarrolladores nuevos. Con ese fin, quitamos 231 líneas de código obsoleto de la biblioteca dart:io
. Si todavía usas estas API que dejarán de estar disponibles, puedes actualizar a las nuevas con dart fix
. También continuamos con nuestra iniciativa de quitar las herramientas obsoletas de la CLI de Dart. Esta vez, quitamos la herramienta dartdoc
(puedes usar dart doc
en su lugar) y la herramienta pub
(puedes usar dart pub
o flutter pub
).
Ampliación de la integración y compatibilidad con plataformas
Un segundo tema fundamental es la integración y compatibilidad con plataformas. Dart es un verdadero lenguaje multiplataforma. A pesar de que ya es compatible con una amplia gama de plataformas, buscamos evolucionar constantemente para garantizar la integración con cada plataforma compatible y admitir plataformas nuevas.
La FFI de Dart (nuestro principal mecanismo de interoperabilidad con el código C nativo) es una forma popular de integrar el código de Dart con el código nativo existente de la plataforma. En Flutter, esta es una excelente manera de compilar complementos que usen API nativas desde la plataforma de alojamiento (como las API win32 de Windows). En Dart 2.17 y Flutter 3, agregamos plantillas a la herramienta flutter
para que ahora puedas crear complementos de FFI que tengan una API de Dart con el respaldo de llamadas dart:ffi
al código nativo. Para obtener más detalles, consulta la página actualizada sobre el desarrollo de paquetes y complementos en flutter.dev.
Con el fin de habilitar el uso de FFI en plataformas que tienen tipos específicos para su ABI (interfaz binaria de la aplicación), FFI ahora admite tipos específicos para ABI. Por ejemplo, ahora puedes usar Long
(long
en C) a fin de representar correctamente un número entero con un tamaño específico para ABI (que podría ser 32 bits o 64 bits, según la arquitectura de la CPU). Si deseas ver todos los tipos compatibles, consulta la lista de "Implementadores" en la página de API de AbiSpecificInteger
.
Cuando uses la FFI de Dart para integraciones completas con plataformas nativas, deberás alinear la limpieza de la memoria y otros recursos (puertos, archivos, etc.) asignados por Dart y el código nativo. Históricamente, esto resultó siempre muy complicado debido a que Dart es un lenguaje de recolección de elementos no utilizados que realiza la limpieza de forma automática. La solución que aporta Dart 2.17 es la introducción del concepto de un método de finalización, que incluye una interfaz de marcadores Finalizable
para "etiquetar" objetos que no deberían finalizarse o descartarse con demasiada anticipación y otros de clase NativeFinalizer
que se pueden adjuntar a un objeto de Dart para ejecutar una devolución de llamada cuando el objeto esté a punto de recolectarse como elemento no utilizado. Juntos, permiten la ejecución del código de limpieza tanto para el código de Dart como para el nativo. Si deseas obtener información detallada, consulta la descripción y los ejemplos en la documentación de API sobre NativeFinalizer
o la documentación sobre WeakReferences
y Finalizer
para obtener asistencia similar respecto del código común de Dart.
Las apps de Flutter tienen un excelente rendimiento al iniciarse y una renderización rápida gracias a nuestra compatibilidad con la compilación de Dart en código nativo. Un segundo caso de uso es la posibilidad de compilar Dart en ejecutables con dart compile
. Estos archivos pueden ejecutarse de manera independiente en cualquier máquina sin la necesidad de instalar el SDK de Dart. Otra novedad de Dart 2.17 es la compatibilidad con la firma de ejecutables, que permite su implementación en Windows y macOS, donde suele ser necesaria.
Además, para mantenemos a la vanguardia en relación con las nuevas plataformas, continuamos ampliando la cantidad de plataformas compatibles. RISC-V es un nuevo conjunto de instrucciones innovador para procesadores. Debido a que RISC-V International, una organización global sin fines de lucro, es propietaria de la especificación RISC-V, el conjunto de instrucciones es gratuito y abierto. Aún se trata de una plataforma emergente, pero confiamos en su potencial; por lo tanto, nuestra versión 2.17.0–266.1.beta
para Linux (o versiones posteriores de nuestro canal beta) será compatible de modo experimental. Nos encantaría recibir tus comentarios. Puedes contarnos tu experiencia mediante la presentación de un problema o en una publicación.
¡Empieza a utilizar Dart 2.17!
Esperamos que la nueva versión 2.17 de Dart te entusiasme, mejore tu productividad y favorezca aún más la integración de tus apps con plataformas. Como primer paso, puedes descargar la versión 2.17 de Dart u obtenerla como parte de la versión actual del SDK Flutter 3.
También te invitamos a consultar el contenido nuevo que preparamos para Google I/O.