Flutter 3.38 and Dart 3.10 are here!Learn more
Adding Flutter to any web application
Flutter views and web content can be composed to produce a web application in different ways. Choose one of the following depending on your use-case:
- A Flutter view controls the full page (full page mode)
- Adding Flutter views to an existing web application (embedded mode)
Full page mode
#In full page mode, the Flutter web application takes control of the whole browser window and covers its viewport completely when rendering.
This is the default embedding mode for new Flutter web projects, and no additional configuration is needed.
<!DOCTYPE html><html><head></head><body><script src="flutter_bootstrap.js" defer></script></body></html> When Flutter web is launched without referencingmultiViewEnabled or ahostElement, it uses full page mode.
To learn more about theflutter_bootstrap.js file, check outCustomize app initialization.
iframe embedding
# Full page mode is recommended when embedding a Flutter web application through aniframe. The page that embeds theiframe can size and position it as needed, and Flutter will fill it completely.
<iframe src="https://url-to-your-flutter/index.html"></iframe> To learn more about the pros and cons of aniframe, check out theInline Frame element docs on MDN.
Embedded mode
# Flutter web applications can also render content into an arbitrary number of elements (commonlydivs) of another web application; this is called "embedded mode" (or "multi-view").
In this mode:
- A Flutter web application can launch, but doesn't render until the first "view" is added, with
addView. - The host application can add or remove views from the embedded Flutter web application.
- The Flutter application is notified when views are added or removed, so it can adjust its widgets accordingly.
Enable multi-view mode
# Enable multi-view mode settingmultiViewEnabled: true in theinitializeEngine method as shown:
{{flutter_js}}{{flutter_build_config}}_flutter.loader.load({onEntrypointLoaded:asyncfunctiononEntrypointLoaded(engineInitializer){letengine=awaitengineInitializer.initializeEngine({multiViewEnabled:true,// Enables embedded mode.});letapp=awaitengine.runApp();// Make this `app` object available to your JS app.}});Manage Flutter views from JS
#To add or remove views, use theapp object returned by therunApp method:
// Adding a view...letviewId=app.addView({hostElement:document.querySelector('#some-element'),});// Removing viewId...letviewConfig=app.removeView(viewId);Handling view changes from Dart
# View additions and removals are surfaced to Flutter through thedidChangeMetrics method of theWidgetsBinding class.
The complete list of views attached to your Flutter app is available through theWidgetsBinding.instance.platformDispatcher.views iterable. These views are oftypeFlutterView.
To render content into eachFlutterView, your Flutter app needs to create aView widget.View widgets can be grouped together under aViewCollection widget.
The following example, from theMulti View Playground, encapsulates the above in aMultiViewApp widget that can be used as the root widget for your app. AWidgetBuilder function runs for eachFlutterView:
import'dart:ui'showFlutterView;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].classMultiViewAppextendsStatefulWidget{constMultiViewApp({super.key,requiredthis.viewBuilder});finalWidgetBuilderviewBuilder;@overrideState<MultiViewApp>createState()=>_MultiViewAppState();}class_MultiViewAppStateextendsState<MultiViewApp>withWidgetsBindingObserver{@overridevoidinitState(){super.initState();WidgetsBinding.instance.addObserver(this);_updateViews();}@overridevoiddidUpdateWidget(MultiViewAppoldWidget){super.didUpdateWidget(oldWidget);// Need to re-evaluate the viewBuilder callback for all views._views.clear();_updateViews();}@overridevoiddidChangeMetrics(){_updateViews();}Map<Object,Widget>_views=<Object,Widget>{};void_updateViews(){finalMap<Object,Widget>newViews=<Object,Widget>{};for(finalFlutterViewviewinWidgetsBinding.instance.platformDispatcher.views){finalWidgetviewWidget=_views[view.viewId]??_createViewWidget(view);newViews[view.viewId]=viewWidget;}setState((){_views=newViews;});}Widget_createViewWidget(FlutterViewview){returnView(view:view,child:Builder(builder:widget.viewBuilder,),);}@overridevoiddispose(){WidgetsBinding.instance.removeObserver(this);super.dispose();}@overrideWidgetbuild(BuildContextcontext){returnViewCollection(views:_views.values.toList(growable:false));}} For more information, check outWidgetsBinding mixin in the API docs, or theMulti View Playground repo that was used during development.
ReplacerunApp byrunWidget in Dart
# Flutter'srunApp function assumes that there's at least one view available to render into (theimplicitView), however in Flutter web's multi-view mode, theimplicitView doesn't exist anymore, sorunApp will start failing withUnexpected null value errors.
In multi-view mode, yourmain.dart must call therunWidget function instead. It doesn't require animplicitView, and will only render into the views that have been explicitly added into your app.
The following example uses theMultiViewApp described above to render copies of theMyApp() widget on everyFlutterView available:
voidmain(){runWidget(MultiViewApp(viewBuilder:(BuildContextcontext)=>constMyApp(),),);}Identifying views
# EachFlutterView has an identifier assigned by Flutter when attached. ThisviewId can be used to uniquely identify each view, retrieve its initial configuration, or decide what to render in it.
TheviewId of the renderedFlutterView can be retrieved from itsBuildContext like this:
classSomeWidgetextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){// Retrieve the `viewId` where this Widget is being built:finalintviewId=View.of(context).viewId;// ... Similarly, from theviewBuilder method of theMultiViewApp, theviewId can be retrieved like this:
MultiViewApp(viewBuilder:(BuildContextcontext){// Retrieve the `viewId` where this Widget is being built:finalintviewId=View.of(context).viewId;// Decide what to render based on `viewId`...},) Read more about theView.of constructor.
Initial view configuration
# Flutter views can receive any initialization data from JS when starting up. The values are passed through theinitialData property of theaddView method, as shown:
// Adding a view with initial data...letviewId=app.addView({hostElement:someElement,initialData:{greeting:'Hello, world!',randomValue:Math.floor(Math.random()*100),}}); In Dart, theinitialData is available as aJSAny object, accessible through the top-levelviews property in thedart:ui_web library. The data is accessed through theviewId of the current view, as shown:
finalinitialData=ui_web.views.getInitialData(viewId)asYourJsInteropType; To learn how to define theYourJsInteropType class to map theinitialData object passed from JS so it's type-safe in your Dart program, check out:JS Interoperability on dart.dev.
View constraints
# By default, an embedded Flutter web view considers the size of itshostElement as an immutable property, and tightly constrains its layout to the available space.
On the web, it's common for the intrinsic size of an element to affect the layout of the page (likeimg orp tags that can reflow content around them).
When adding a view to Flutter web, you might configure it with constraints that inform Flutter of how the view needs to be laid out:
// Adding a view with initial data...letviewId=app.addView({hostElement:someElement,viewConstraints:{maxWidth:320,minHeight:0,maxHeight:Infinity,}}); The view constraints passed from JS need to be compatible with the CSS styling of thehostElement where Flutter is being embedded. For example, Flutter won’t try to "fix" contradictory constants like passingmax-height: 100px in CSS, butmaxHeight: Infinity to Flutter.
To learn more, check out theViewConstraints class, andUnderstanding constraints.
Custom element (hostElement)
#As of the Flutter 3.10 release, you can embed a single-view Flutter web app into any HTML element of your web page.
To tell Flutter web which element to render into, pass an object with aconfig field to the_flutter.loader.load function that specifies aHTMLElement as thehostElement.
_flutter.loader.load({config:{hostElement:document.getElementById('flutter_host'),}});Multi-view embedding also works with a single view. An advantage of embedding a single-view by using multi-view support, is that you can create and remove views dynamically. Also, if single view support is ever deprecated, it won't affect your app.
To learn more about other configuration options, check outCustomizing web app initialization.
Unless stated otherwise, the documentation on this site reflects Flutter 3.38.1. Page last updated on 2025-9-25.View source orreport an issue.