Migrar a package:web
Cómo migrar el código de interoperabilidad web de dart:html a package:web.
El paquete package:web de Dart expone el acceso a las APIs del navegador,
permitiendo la interoperabilidad entre las aplicaciones Dart y la web.
Usa package:web para interactuar con el navegador y
manipular objetos y elementos en el DOM.
import 'package:web/web.dart';
void main() {
final div = document.querySelector('div')!;
div.text = 'Text set at ${DateTime.now()}';
}
package:web vs dart:html
#
El objetivo de package:web es renovar cómo expone Dart las APIs web
abordando varias preocupaciones con las librerías web de Dart existentes:
-
Wasm compatibility
Los paquetes solo pueden ser compatibles con Wasm si usan
dart:js_interopydart:js_interop_unsafe.package:webse basa endart:js_interop, por lo que, de forma predeterminada, es compatible condart2wasm.Las librerías web principales de Dart, como
dart:htmlydart:svg, están depreciadas y no están soportadas cuando se compila a Wasm. -
Staying modern
package:webutiliza el Web IDL para generar automáticamente miembros de interoperabilidad y tipos de interoperabilidad para cada declaración en el IDL. Generar referencias directamente, a diferencia de los miembros y abstracciones adicionales endart:html, permite quepackage:websea más conciso, fácil de entender, más consistente y más capaz de mantenerse al día con el futuro de los desarrollos web. -
Versioning
Debido a que es un paquete,
package:webpuede tener un control de versiones más sencillo que una librería comodart:htmly evitar romper el código del usuario a medida que evoluciona. También hace que el código sea menos exclusivo y más abierto a las contribuciones. Los desarrolladores pueden crear sus propias declaraciones de interoperabilidad alternativas y usarlas junto conpackage:websin conflictos.
Estas mejoras resultan naturalmente en algunas
diferencias de implementación entre package:web y dart:html.
Los cambios que más afectan a los paquetes existentes,
como los renombramientos en IDL y
las pruebas de tipo,
se abordan en las secciones de migración siguientes. Aunque solo nos referimos a
dart:html por brevedad, los mismos patrones de migración se aplican a cualquier otra librería
web principal de Dart como dart:svg.
Migración desde dart:html
#
Elimina la importación de dart:html y reemplázala con package:web/web.dart:
import 'dart:html' as html; // Remove
import 'package:web/web.dart' as web; // Add
Agrega web a las dependencies (dependencias) en tu pubspec:
dart pub add web
Las siguientes secciones cubren algunos de los problemas comunes de migración
de dart:html a package:web.
Para cualquier otro problema de migración, consulta el repositorio dart-lang/web y reporta un problema (file an issue).
Renombramientos
#Muchos de los símbolos en dart:html fueron renombrados de
su declaración original en IDL para alinearse más con el estilo de Dart.
Por ejemplo, appendChild se convirtió en append,
HTMLElement se convirtió en HtmlElement, etc.
Por el contrario, para reducir la confusión,
package:web utiliza los nombres originales de las definiciones IDL.
Hay un dart fix disponible para convertir tipos que han sido renombrados
entre dart:html y package:web.
Después de cambiar la importación, cualquier objeto renombrado generará nuevos errores de "no definido" (undefined). Puedes solucionar esto ya sea:
- Desde la CLI, ejecutando
dart fix --dry-run. - En tu IDE, seleccionando el
dart fix: Rename to 'package:web name'.
La herramienta dart fix cubre muchos de los renombramientos de tipos comunes.
Si te encuentras con un tipo de dart:html que no tiene una corrección de dart fix
para renombrarlo,
primero háznoslo saber reportando un problema (issue).
Luego, puedes intentar descubrir manualmente el nombre del tipo en package:web de un
miembro existente en dart:html buscando su definición.
El valor de la anotación @Native en la definición de un miembro de dart:html
le indica al compilador que trate cualquier objeto JS de ese tipo como la clase de Dart
que está anotando. Por ejemplo, la anotación @Native nos dice que el
nombre JS nativo del miembro HtmlElement de dart:html es HTMLElement,
por lo que el nombre en package:web también será HTMLElement:
@Native("HTMLElement")
class HtmlElement extends Element implements NoncedElement { }
Para encontrar la definición en dart:html de un miembro no definido en package:web,
prueba cualquiera de los siguientes métodos:
- Presiona Ctrl o Command y haz clic en el nombre no definido en el IDE y elige Ir a la definición (Go to Definition).
- Busca el nombre en la documentación de la API de
dart:htmly revisa su página bajo Annotations.
Del mismo modo, es posible que encuentres una API no definida en package:web cuya definición del
miembro correspondiente en dart:html utilice la palabra clave native.
Verifica si la definición utiliza la anotación @JSName para un renombramiento;
el valor de la anotación te dirá el nombre que el miembro utiliza en
package:web:
@JSName('appendChild')
Node append(Node node) native;
native es una palabra clave interna que significa lo mismo que external en este
contexto.
Element y HTMLElement
#
Element ahora es HTMLElement, pero el extension type Element también existe como
un tipo base para HTMLElement, SVGElement, etc.
Consulta la documentación de MDN de Element.
querySelector devuelve un Element, ya que puede devolver otros subtipos de
Element
además de HTMLElement como SVGElement. Se necesita una conversión hacia abajo (downcast) a HTMLElement
si necesitas acceder a sus métodos.
element.querySelector('#selectme')!.className = 'test'; // valid in both
element.querySelector('#selectme')!.style.color = 'red'; // Remove
(element.querySelector('#selectme') as HTMLElement).style.color = 'red'; // Add
Operaciones de lista
#A diferencia de dart:html, los métodos de package:web como Element.querySelectorAll
y
Element.children devuelven valores que no implementan List. Si se requiere una
List, deberás envolverla con una clase.
Para operaciones inmutables, puedes usar JSImmutableListWrapper:
final anchors = document.querySelectorAll('a');
for (final anchor in anchors) {} // Remove
for (final anchor in JSImmutableListWrapper(anchors)) {} //Add
for (final child in parent.children) {} // Remove
for (final child in JSImmutableListWrapper(parent.children)) {} // Add
Debes agregar la implementación para operaciones mutables simples como removeWhere:
parent.children.removeWhere(test); // Remove
for (var i = parent.children.length - 1; i >= 0; --i) {
if (test(parent.children.item(i)!)) {
parent.children.item(i)!.remove();
}
} // Add
Ejemplos comunes de manipulación del DOM
#Aquí hay algunos ejemplos comunes de cambios triviales que deben realizarse al
migrar de dart:html a package:web:
element.querySelector('#selector')?.innerHtml = 'something'; // Remove
element.querySelector('#selector')?.innerHTML = 'something'.toJS; // Add
element.parent.classes.add('class'); // Remove
element.parentElement.classList.add('class'); // Add
element.appendHtml(html); // Remove
element.insertAdjacentHTML('beforeend', html.toJS); // Add
var checkbox = CheckboxInputElement(); // Remove
var checkbox = HTMLInputElement()..type='checkbox'; // Add
element.querySelectorAll('a').classes.add('link'); // Remove
for (final a in JSImmutableListWrapper(element.querySelectorAll('a'))) {
a.classList.add('a');
} // Add
Pruebas de tipo
#Es común que el código que utiliza dart:html emplee comprobaciones en tiempo de ejecución como is.
Cuando se usa con un objeto de dart:html, is y as
verifican que el objeto sea el tipo de JS dentro de la anotación @Native.
En cambio, todos los tipos de package:web se materializan (reify) como JSObject. Esto significa que una
prueba de tipo en tiempo de ejecución dará como resultado un comportamiento diferente entre los tipos de dart:html
y package:web.
Para poder realizar pruebas de tipo, migra cualquier código de dart:html
que use pruebas de tipo is para usar métodos de interoperabilidad
como instanceOfString
o el ayudante isA más conveniente y tipado
(disponible a partir de Dart 3.4).
La sección Compatibilidad, comprobaciones de tipo y conversiones de tipo (casts)
de la página de tipos de JS cubre las alternativas en detalle.
obj is Window; // Remove
obj.instanceOfString('Window'); // Add
element is InputElement; // Remove
element.isA<HTMLInputElement>(); // Add
Firmas de tipo
#Muchas APIs en dart:html admiten varios tipos de Dart en sus firmas de tipo.
Debido a que dart:js_interop restringe
los tipos que se pueden escribir, algunos de
los miembros en package:web ahora requerirán que conviertas
el valor antes de llamar al miembro.
Aprende cómo usar los métodos de conversión de interoperabilidad en la sección Conversiones
de la página de tipos de JS.
window.addEventListener('click', callback); // Remove
window.addEventListener('click', callback.toJS); // Add
Generalmente, puedes detectar qué métodos necesitan una conversión porque estarán marcados con alguna variación de la excepción:
A value of type '...' can't be assigned to a variable of type 'JSFunction?'
Importaciones condicionales
#Es común que el código utilice una importación condicional basada en si dart:html
está soportado para diferenciar entre nativo y web:
export 'src/hw_none.dart'
if (dart.library.io) 'src/hw_io.dart'
if (dart.library.html) 'src/hw_html.dart';
Sin embargo, dado que dart:html está depreciado y no está soportado al
compilar a Wasm, la alternativa correcta ahora es
usar dart.library.js_interop para diferenciar entre nativo y web:
export 'src/hw_none.dart' // Stub implementation
if (dart.library.io) 'src/hw_io.dart' // dart:io implementation
if (dart.library.js_interop) 'src/hw_web.dart'; // package:web implementation
Despacho virtual y simulación (mocking)
#Las clases de dart:html admitían el despacho virtual, pero debido a que JS interop utiliza
extension types, el despacho virtual no es posible. Del mismo modo, las llamadas
dynamic con tipos de package:web no funcionarán como se espera (o podrían continuar funcionando
solo por casualidad, pero dejarán de hacerlo cuando se elimine dart:html), ya que sus miembros
solo están disponibles estáticamente. Migra todo el código que dependa del despacho virtual para
evitar este problema.
Un caso de uso del despacho virtual es la simulación (mocking). Si tienes una clase de simulación que
implementa una clase de dart:html, no se puede usar para implementar un tipo de
package:web. En su lugar, prefiere simular el propio objeto de JS. Consulta el tutorial de simulación (mocking)
para obtener más información.
APIs no nativas
#
Las clases de dart:html también podrían contener APIs que tienen una implementación
no trivial. Estos miembros pueden o no existir en los ayudantes (helpers) de
package:web. Si tu código depende de los detalles de esa
implementación, es posible que puedas copiar el código necesario.
Sin embargo, si crees que eso no es viable o si ese código también sería beneficioso
para otros usuarios, considera reportar un problema (file an issue) o subir un pull request a
package:web para dar soporte a ese miembro.
Zonas (Zones)
#En dart:html, los callbacks se zonifican automáticamente.
Este no es el caso en package:web. No hay una vinculación automática de
callbacks en la zona actual.
Si esto es importante para tu aplicación, aún puedes usar zonas, pero tendrás que escribirlas tú mismo vinculando el callback. Consulta #54507 para obtener más detalles. Aún no hay una API de conversión o ayudante (helper) disponible para hacer esto de forma automática.
Ayudantes (Helpers)
#El núcleo de package:web contiene miembros de interoperabilidad external,
pero no proporciona otras funcionalidades que dart:html proporcionaba de forma predeterminada.
Para mitigar estas diferencias, package:web contiene ayudantes (helpers)
para brindar soporte adicional en el manejo de una serie de casos de uso
que no están disponibles directamente a través de la interoperabilidad del núcleo.
La librería de ayudantes contiene varios miembros para exponer algunas características heredadas (legacy) de
las librerías web de Dart.
Por ejemplo, el núcleo de package:web solo tiene soporte para agregar y eliminar
escuchas de eventos (event listeners). En su lugar, puedes usar los ayudantes de streams (stream helpers)
que facilitan la suscripción a eventos con Streams de Dart sin tener que escribir ese código tú mismo.
// Original dart:html version:
final htmlInput = InputElement();
await htmlInput.onBlur.first;
// Migrated package:web version:
final webInput = HTMLInputElement();
await webInput.onBlur.first;
Puedes encontrar todos los ayudantes y su documentación en el repositorio en
package:web/helpers. Se actualizarán continuamente para ayudar a los usuarios
en la migración y facilitar el uso de las APIs web.
Ejemplos
#Aquí hay algunos ejemplos de paquetes que han sido migrados de dart:html
a package:web:
A menos que se indique lo contrario, la documentación en este sitio refleja Dart 3.12.2. Página actualizada por última vez el 2026-05-15. Ver fuente oreportar un problema.