Uso da interop JS
Como declarar e usar membros de interop JS.
A biblioteca dart:js_interop permite chamar APIs JavaScript e interagir com
valores JS usando sintaxe idiomática do Dart.
Para acessar uma API JavaScript a partir do Dart, exponha-a no escopo global do JS
e
use os seguintes mecanismos:
-
Declare membros de interop
externalpara chamar APIs JS e receber valores JS. - Use ou declare tipos de interop para fornecer tipos e interfaces do Dart para valores JS.
- Use funções de conversão para converter valores entre Dart e JS, a menos que uma assinatura de interop use tipos primitivos permitidos.
Tipos de interop
#Ao interagir com um valor JS, você precisa fornecer um tipo Dart para ele. Você pode fazer isso usando ou declarando um tipo de interop. Tipos de interop são um tipo JS fornecido pelo Dart ou um extension type que envolve um tipo de interop.
Tipos de interop permitem fornecer uma interface para um valor JS e declarar APIs de interop para os membros dele. Você também os usa nas assinaturas de outras APIs de interop:
extension type Window(JSObject _) implements JSObject {}
O extension type Window é um tipo de interop para uma instância arbitrária de
JSObject.
O compilador não oferece uma garantia em tempo de execução
de que um valor Window
seja de fato um objeto Window
do JS,
e definir várias interfaces de interop para o mesmo valor JS subjacente
não causa conflito.
Se você quiser verificar se um valor Window é de fato um objeto Window
do JS,
pode verificar o tipo do valor JS por meio da interop.
Você também pode declarar seu próprio tipo de interop para os tipos JS que o Dart fornece, envolvendo-os:
extension type Array._(JSArray<JSAny?> _) implements JSArray<JSAny?> {
external Array();
}
Na maioria dos casos, declare um tipo de interop usando
o tipo JSObject como tipo de representação
ao
interagir com objetos JS que não têm um tipo de interop fornecido pelo Dart.
Além disso, faça os tipos de interop implementar o tipo de representação deles para que
você possa usá-los onde uma API espera o tipo de representação,
como em muitas APIs fornecidas pelo package:web.
Membros de interop
#
Membros de interop declarados com a palavra-chave external
oferecem uma
sintaxe idiomática para membros JS, permitindo escrever uma assinatura de tipo Dart para
seus argumentos e valores de retorno.
O compilador impõe restrições
aos tipos que você pode usar na
assinatura desses membros.
A combinação de onde você declara um membro de interop, seu nome,
qual tipo de membro Dart ele é e quaisquer renomeações determina
a qual API JS ele corresponde.
Membros de interop de nível superior
#Considere os seguintes membros JS:
globalThis.name = 'global';
globalThis.isNameEmpty = function() {
return globalThis.name.length == 0;
}
Você pode escrever membros de interop para eles, como mostrado no exemplo a seguir:
@JS()
external String get name;
@JS()
external set name(String value);
@JS()
external bool isNameEmpty();
Aqui, o escopo global expõe a propriedade name e a função isNameEmpty.
Para acessá-las, você usa membros de interop de nível superior.
Para obter e definir a propriedade name, declare e use
um getter e um setter de interop com o mesmo nome.
Para usar a função isNameEmpty, declare e chame
uma função de interop com o mesmo nome.
Você pode declarar getters, setters, métodos e campos de interop de nível superior.
Campos de interop são equivalentes a pares de getter e setter.
Declare membros de interop de nível superior com a anotação @JS() para
distingui-los de outros membros external de nível superior,
como os que usam a biblioteca dart:ffi.
Membros de tipos de interop
#Considere a seguinte classe Time do JS:
class Time {
constructor(hours, minutes) {
this._hours = Math.abs(hours) % 24;
this._minutes = arguments.length == 1 ? 0 : Math.abs(minutes) % 60;
}
static dinnerTime = new Time(18, 0);
static getTimeDifference(t1, t2) {
return new Time(t1.hours - t2.hours, t1.minutes - t2.minutes);
}
get hours() {
return this._hours;
}
set hours(value) {
this._hours = Math.abs(value) % 24;
}
get minutes() {
return this._minutes;
}
set minutes(value) {
this._minutes = Math.abs(value) % 60;
}
isDinnerTime() {
return this.hours == Time.dinnerTime.hours && this.minutes == Time.dinnerTime.minutes;
}
}
// Need to expose the type to the global scope.
globalThis.Time = Time;
Você pode escrever uma interface de interop para a classe Time, como mostrado no
exemplo a seguir:
extension type Time._(JSObject _) implements JSObject {
external Time(int hours, int minutes);
external factory Time.onlyHours(int hours);
external static Time dinnerTime;
external static Time getTimeDifference(Time t1, Time t2);
external int hours;
external int minutes;
external bool isDinnerTime();
bool isMidnight() => hours == 0 && minutes == 0;
}
Dentro de um tipo de interop, você pode declarar vários tipos diferentes de
membros de interop external.
Construtores
#
Construtores posicionais criam um novo objeto JS chamando new no
nome JS do extension type:
final time = Time(0, 0); // Calls `new Time(0, 0)`
final midnight = Time.onlyHours(0); // Calls `new Time(0)`
Construtores sem nome, construtores nomeados e construtores factory com
parâmetros posicionais seguem a mesma semântica de invocação JS.
Construtores de objeto literal
#
Para criar um objeto literal
do JS com propriedades específicas, declare um
construtor (com ou sem a palavra-chave factory) que receba apenas parâmetros
nomeados correspondentes aos nomes das propriedades JS:
extension type Options._(JSObject o) implements JSObject {
external Options({int a, int b});
external int get a;
external int get b;
}
final full = Options(a: 0, b: 1); // Creates `{a: 0, b: 1}`
final partial = Options(a: 0); // Creates `{a: 0}`
Apenas os argumentos que você passa são incluídos como propriedades no objeto JS
criado.
Você pode ler ou modificar essas propriedades por meio de membros de instância external.
Membros estáticos
#
Assim como os construtores, métodos static, getters, setters e campos usam o
nome JS do extension type:
final dinner = Time.dinnerTime; // Gets `Time.dinnerTime`
// Calls `Time.getTimeDifference(t1, t2)`
final diff = Time.getTimeDifference(t1, t2);
Membros de instância
#Métodos, getters, setters e campos de instância obtêm, definem ou invocam propriedades em uma instância de objeto JS:
final time = Time(0, 0);
print(time.isDinnerTime()); // Calls `time.isDinnerTime()` -> false
final dinnerTime = Time.dinnerTime;
time.hours = dinnerTime.hours; // Gets `dinnerTime.hours` and sets `time.hours`
time.minutes = dinnerTime.minutes;
print(time.isDinnerTime()); // true
Operadores
#
Tipos de interop suportam apenas dois operadores de interop external: [] e
[]=,
que correspondem aos acessadores de propriedade
do JavaScript:
extension type Array(JSArray<JSNumber> _) implements JSArray<JSNumber> {
external JSNumber operator [](int index);
external void operator []=(int index, JSNumber value);
}
final first = array[0]; // Reads `array[0]`
array[0] = 42.toJS; // Sets `array[0] = 42`
A biblioteca dart:js_interop expõe outros operadores JS por meio de
funções utilitárias.
Tipos de interop também podem declarar membros não-external, como o
método isMidnight no exemplo de Time.
Membros de extension em tipos de interop
#
Você também pode escrever membros external em extensions
de tipos de interop,
incluindo extensions sobre um parâmetro de tipo genérico limitado por um tipo de interop,
como mostrado no exemplo a seguir:
extension on Array {
external int push(JSAny? any);
}
extension <T extends JSObject> on T {
external T clone();
}
Chamar o método push se comporta da mesma forma que se você
o tivesse declarado diretamente no extension type Array.
De modo semelhante, chamar o método clone em qualquer subtipo do tipo
JSObject
invoca o método clone do JS
e preserva o tipo estático T do receptor.
Extensions podem ter membros de instância e operadores external, mas
não podem ter membros static nem construtores external.
Assim como nos tipos de interop, você pode
escrever quaisquer membros não-external na extension.
Extensions são úteis quando um tipo de interop não
expõe o membro external de que você precisa e você não quer
criar um novo tipo de interop.
Parâmetros
#
Um método external de interop pode conter apenas argumentos posicionais e opcionais.
Essa restrição existe porque membros JS recebem apenas argumentos posicionais.
A única exceção são os construtores de object literal,
que aceitam apenas argumentos nomeados.
Diferente dos métodos não-external, o compilador omite argumentos opcionais
em vez de substituí-los pelos valores padrão.
Por exemplo, considere o seguinte método:
external int push(JSAny? any, [JSAny? any2]);
Chamar array.push(0.toJS) no Dart resulta em
uma invocação JS de array.push(0), e não de array.push(0, null).
Esse comportamento evita a necessidade de escrever vários membros de interop para
a mesma API JS só para omitir argumentos null.
Se você declarar um parâmetro com um valor padrão explícito,
o compilador avisa que ele ignora esse valor.
@JS()
#
A anotação @JS() informa ao compilador que uma declaração é
um membro ou tipo de interop JS e, opcionalmente, a renomeia ou a coloca em um namespace no JS:
-
Em declarações de nível superior, a anotação
@JS()é obrigatória (com ou sem um valor string) para distingui-las de outras declaraçõesexternal, como as da bibliotecadart:ffi. -
Em tipos de interop e membros de extension, a anotação
@JS()é opcional quando o nome no Dart é igual ao nome no JS, porque o compilador infere a interop JS a partir do tipo de representação ou do tipoon.
Renomear membros e tipos de interop
#
Passe uma string constante para a anotação @JS()
quando o nome no Dart precisar ser diferente do nome no JS.
Por exemplo, você pode sobrecarregar um método JS com várias assinaturas no Dart,
definir vários tipos de interop para a mesma interface JS,
ou vincular um identificador JS como $a que não é um nome válido no Dart:
extension type Array._(JSArray<JSAny?> _) implements JSArray<JSAny?> {
external int push(JSNumber number);
@JS('push')
external int pushString(JSString string);
}
Os métodos push e pushString invocam push no JS.
Você também pode renomear um tipo de interop:
@JS('Date')
extension type JSDate._(JSObject _) implements JSObject {
external JSDate(); // Calls `new Date()`
external static int now(); // Calls `Date.now()`
}
Como os construtores sempre invocam o nome JS do extension type que os contém,
você não pode colocar uma anotação @JS() em um construtor ou factory
external
de um extension type de interop.
Anote a declaração do extension type.
Colocar uma biblioteca em um namespace
#
Coloque a anotação @JS() em uma diretiva library para adicionar um prefixo de namespace a todos os
membros de interop de nível superior, tipos de interop e membros static
de interop
nessa biblioteca.
Você também pode usar namespaces separados por . em bibliotecas, membros de
nível superior e tipos de interop:
@JS('library1.library2')
library;
import 'dart:js_interop';
@JS('library3.method')
external void method(); // Calls `library1.library2.library3.method()`
@JS('library3.JSType')
extension type JSType._(JSObject _) implements JSObject {
// Calls `new library1.library2.library3.JSType()`
external JSType();
// Gets `library1.library2.library3.JSType.staticMember`
external static int get staticMember;
}
Tenha em mente estas regras de namespace:
-
Um nome de
librarydo Dart (comolibrary interop_library;) nunca é usado como namespace do JS, a menos que você forneça uma string não vazia em@JS('...'). -
Você também pode usar nomes separados por
.nas anotações@JS()de membros de tipos de interop e de membros de extension. -
Se você omitir o valor em
@JS()ou passar uma string vazia, o compilador usa o nome Dart da declaração.
Exportar funções e objetos do Dart para o JS
#
As seções anteriores mostram como chamar membros JS a partir do Dart.
Também é útil exportar código Dart para que você possa chamá-lo a partir do JS.
Para exportar uma função Dart para o JS, primeiro converta-a usando o
getter Function.toJS
(que também converte qualquer Future retornado em um
JSPromise), ou usando o getter toJSVarArgs para callbacks variádicos síncronos.
Depois, passe a função encapsulada para o JS por meio de um membro de interop.
A partir daí, o código JS pode chamar a função.
Por exemplo, o código a seguir exporta funções Dart síncronas, assíncronas e variádicas para propriedades globais do JS e as chama a partir do JS:
import 'dart:js_interop';
@JS()
external set exportedFunction(JSFunction value);
@JS()
external set asyncFunction(JSFunction value);
@JS()
external set joinAll(JSFunction value);
void printString(JSString string) {
print(string.toDart);
}
Future<JSString> fetchGreeting(JSString name) async =>
'Hello, ${name.toDart}!'.toJS;
JSString joinArgs(JSArray<JSString> args) =>
args.toDartStringList.join(', ').toJS;
void main() {
exportedFunction = printString.toJS;
asyncFunction = fetchGreeting.toJS;
joinAll = joinArgs.toJSVarArgs;
}
globalThis.exportedFunction('hello world');
await globalThis.asyncFunction('Dash');
globalThis.joinAll('a', 'b', 'c'); // 'a, b, c'
Funções exportadas dessa forma têm restrições de tipo semelhantes às dos membros de interop.
Às vezes, é útil exportar uma interface Dart inteira para que
o JS possa interagir com um objeto Dart.
Para isso, marque a classe Dart como exportável usando a anotação @JSExport
e envolva instâncias dessa classe usando a função createJSInteropWrapper.
Para uma explicação mais detalhada dessa técnica, incluindo como
fazer mock de valores JS, consulte Como fazer mock de objetos de interop JavaScript.
dart:js_interop e dart:js_interop_unsafe
#
A biblioteca dart:js_interop
contém os membros principais de que você precisa,
incluindo a anotação @JS(), os tipos JS, as funções de conversão e várias
funções utilitárias.
As funções utilitárias incluem:
-
globalContext: representa o escopo global que os compiladores usam para encontrar membros e tipos de interop. - Helpers para inspecionar o tipo de valores JS: examinam o valor JavaScript subjacente de um tipo de interop.
- Operadores JS: expõem operadores JavaScript como membros utilitários.
-
dartifyejsify: verificam o tipo de certos valores JS e os convertem de valores JS para valores Dart ou de valores Dart para valores JS. Prefira usar a conversão específica quando você souber o tipo do valor JS, pois a verificação extra de tipo pode ser custosa. -
importModule: importa módulos dinamicamente como uma instância deJSObject. -
isA: verifica se um valor de interop JS é uma instância do tipo JS especificado pelo argumento de tipo. -
jsIdentical: verifica se dois valores JS são idênticos usando a semântica de igualdade estrita===do JavaScript.
A biblioteca dart:js_interop_unsafe
contém membros que permitem
buscar propriedades dinamicamente.
Por exemplo, você pode acessar a propriedade log de console
usando uma string:
JSFunction f = console['log'] as JSFunction;
Em vez de declarar um membro de interop chamado log,
você pode usar uma string para acessar a propriedade.
A biblioteca dart:js_interop_unsafe também fornece funções para
verificar, obter, definir e chamar propriedades dinamicamente.
Salvo indicação em contrário, a documentação neste site reflete Dart 3.13.7. Página atualizada pela última vez em 2026-11-12. Ver código-fonte ou relatar um problema.
Encontrou essa página sem tradução ou que precisa de correção? Abra uma issue e nos ajude a manter esse site em PT-BR para ajudar pessoas como você.