Agregar Flutter a cualquier aplicación web
Aprende las diferentes formas de incrustar vistas de Flutter en el contenido web.
Las vistas de Flutter y el contenido web se pueden componer para producir una aplicación web de diferentes maneras. Elige una de las siguientes opciones según tu caso de uso:
- Una vista de Flutter controla toda la página (modo de página completa)
- Agregar vistas de Flutter a una aplicación web existente (modo incrustado)
Modo de página completa
#En el modo de página completa, la aplicación web de Flutter toma el control de toda la ventana del navegador y cubre su viewport por completo al renderizar.
Este es el modo de incrustación predeterminado para los nuevos proyectos web de Flutter, y no se necesita ninguna configuración adicional.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="flutter_bootstrap.js" defer></script>
</body>
</html>
Cuando Flutter web se inicia sin hacer referencia a multiViewEnabled o a un
hostElement, utiliza el modo de página completa.
Para obtener más información sobre el archivo flutter_bootstrap.js,
consulta Personalizar la inicialización de la aplicación.
Incrustación con iframe
#
Se recomienda el modo de página completa al incrustar una aplicación web de Flutter a través de un
iframe. La página que incrusta el iframe puede cambiar su tamaño y posicionarlo según sea necesario,
y Flutter lo llenará por completo.
<iframe src="https://url-to-your-flutter/index.html"></iframe>
Para obtener más información sobre los pros y los contras de un iframe,
consulta los documentos del elemento Inline Frame en MDN.
Modo incrustado
#Las aplicaciones web de Flutter también pueden renderizar contenido en un número arbitrario de
elementos (comúnmente divs) de otra aplicación web; esto se denomina "modo
incrustado" (o "multivista").
En este modo:
- Una aplicación web de Flutter puede iniciarse, pero no se renderiza hasta que se agrega la primera
"vista" con
addView. - La aplicación host puede agregar o eliminar vistas de la aplicación web incrustada de Flutter.
- La aplicación de Flutter recibe una notificación cuando se agregan o eliminan vistas, para que pueda ajustar sus widgets en consecuencia.
Habilitar el modo multivista
#Habilita el modo multivista configurando multiViewEnabled: true en el
método initializeEngine como se muestra:
{{flutter_js}}
{{flutter_build_config}}
_flutter.loader.load({
onEntrypointLoaded: async function onEntrypointLoaded(engineInitializer) {
let engine = await engineInitializer.initializeEngine({
multiViewEnabled: true, // Enables embedded mode.
});
let app = await engine.runApp();
// Make this `app` object available to your JS app.
}
});
Gestionar vistas de Flutter desde JS
#Para agregar o eliminar vistas, usa el objeto app devuelto por el método runApp:
// Adding a view...
let viewId = app.addView({
hostElement: document.querySelector('#some-element'),
});
// Removing viewId...
let viewConfig = app.removeView(viewId);
Manejar cambios de vista desde Dart
#Las adiciones y eliminaciones de vistas se exponen a Flutter a través del
método didChangeMetrics
de la clase WidgetsBinding.
La lista completa de vistas adjuntas a tu aplicación Flutter está disponible
a través del iterable WidgetsBinding.instance.platformDispatcher.views.
Estas vistas son de tipo FlutterView.
Para renderizar contenido en cada FlutterView, tu aplicación Flutter necesita crear un
widget View.
Los widgets View se pueden agrupar bajo un
widget ViewCollection.
El siguiente ejemplo, del Multi View Playground, encapsula
lo anterior en un widget MultiViewApp que se puede usar como el widget raíz para
tu aplicación. Se ejecuta una función WidgetBuilder
para cada FlutterView:
import 'dart:ui' show FlutterView;
import 'package:flutter/widgets.dart';
/// Calls [viewBuilder] for every view added to the app to obtain the widget to
/// render into that view. The current view can be looked up with [View.of].
class MultiViewApp extends StatefulWidget {
const MultiViewApp({super.key, required this.viewBuilder});
final WidgetBuilder viewBuilder;
@override
State<MultiViewApp> createState() => _MultiViewAppState();
}
class _MultiViewAppState extends State<MultiViewApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_updateViews();
}
@override
void didUpdateWidget(MultiViewApp oldWidget) {
super.didUpdateWidget(oldWidget);
// Need to re-evaluate the viewBuilder callback for all views.
_views.clear();
_updateViews();
}
@override
void didChangeMetrics() {
_updateViews();
}
Map<Object, Widget> _views = <Object, Widget>{};
void _updateViews() {
final Map<Object, Widget> newViews = <Object, Widget>{};
for (final FlutterView view in WidgetsBinding.instance.platformDispatcher.views) {
final Widget viewWidget = _views[view.viewId] ?? _createViewWidget(view);
newViews[view.viewId] = viewWidget;
}
setState(() {
_views = newViews;
});
}
Widget _createViewWidget(FlutterView view) {
return View(
view: view,
child: Builder(
builder: widget.viewBuilder,
),
);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return ViewCollection(views: _views.values.toList(growable: false));
}
}
Para obtener más información, consulta el mixin WidgetsBinding
en los documentos de la API, o
el repositorio de Multi View Playground que se utilizó durante el desarrollo.
Reemplazar runApp por runWidget en Dart
#
La función runApp de Flutter
asume que hay al menos una vista disponible
para renderizar (la implicitView); sin embargo, en el modo multivista de Flutter web,
la implicitView ya no existe, por lo que runApp comenzará a fallar con
errores de Unexpected null value.
En el modo multivista, tu main.dart debe llamar a la función runWidget
en su lugar. No requiere una implicitView y solo se renderizará en las
vistas que se hayan agregado explícitamente a tu aplicación.
El siguiente ejemplo utiliza la MultiViewApp descrita anteriormente para renderizar
copias del widget MyApp() en cada FlutterView disponible:
void main() {
runWidget(
MultiViewApp(
viewBuilder: (BuildContext context) => const MyApp(),
),
);
}
Identificar vistas
#Cada FlutterView tiene un identificador asignado por Flutter al
adjuntarse. Este viewId se puede usar para identificar de manera única cada vista,
recuperar su configuración inicial o decidir qué renderizar en ella.
El viewId del FlutterView renderizado se puede recuperar de
su BuildContext de la siguiente manera:
class SomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Retrieve the `viewId` where this Widget is being built:
final int viewId = View.of(context).viewId;
// ...
De manera similar, desde el método viewBuilder de la MultiViewApp,
el viewId se puede recuperar así:
MultiViewApp(
viewBuilder: (BuildContext context) {
// Retrieve the `viewId` where this Widget is being built:
final int viewId = View.of(context).viewId;
// Decide what to render based on `viewId`...
},
)
Lee más sobre el constructor View.of.
Configuración inicial de la vista
#Las vistas de Flutter pueden recibir cualquier dato de inicialización desde JS al iniciarse.
Los valores se pasan a través de la propiedad initialData del método addView
como se muestra:
// Adding a view with initial data...
let viewId = app.addView({
hostElement: someElement,
initialData: {
greeting: 'Hello, world!',
randomValue: Math.floor(Math.random() * 100),
}
});
En Dart, el initialData está disponible como un objeto JSAny, accesible a través
de la propiedad de nivel superior views en la biblioteca dart:ui_web. Se accede a los datos
a través del viewId de la vista actual, como se muestra:
final initialData = ui_web.views.getInitialData(viewId) as YourJsInteropType;
Para aprender a definir la clase YourJsInteropType para mapear el objeto initialData
pasado desde JS para que sea seguro con respecto a los tipos en tu programa Dart, consulta:
Interoperabilidad JS en dart.dev.
Restricciones de vista
#De forma predeterminada, una vista web incrustada de Flutter considera el tamaño de su hostElement
como una propiedad inmutable y limita estrictamente su diseño al espacio disponible.
En la web, es común que el tamaño intrínseco de un elemento afecte el
diseño de la página (como las etiquetas img o p que pueden reajustar el contenido a su
alrededor).
Al agregar una vista a Flutter web, puedes configurarla con restricciones que informen a Flutter sobre cómo debe distribuirse la vista:
// Adding a view with initial data...
let viewId = app.addView({
hostElement: someElement,
viewConstraints: {
maxWidth: 320,
minHeight: 0,
maxHeight: Infinity,
}
});
Las restricciones de la vista pasadas desde JS deben ser compatibles con el estilo CSS
del hostElement donde se incrusta Flutter. Por ejemplo, Flutter
no intentará "corregir" constantes contradictorias como pasar max-height: 100px
en CSS, pero maxHeight: Infinity a Flutter.
Para obtener más información, consulta la clase ViewConstraints,
y Entender las restricciones.
Elemento personalizado (hostElement)
#
Puedes incrustar una aplicación web de Flutter de vista única en cualquier elemento HTML de tu página web.
Para indicarle a Flutter web en qué elemento renderizar,
pasa un objeto con un campo config a la función _flutter.loader.load
que especifique un HTMLElement como el hostElement.
_flutter.loader.load({
config: {
hostElement: document.getElementById('flutter_host'),
}
});
Para obtener más información sobre otras opciones de configuración, consulta Personalizar la inicialización de la aplicación web.
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-05. Ver código fuente oreportar un problema.