将 Flutter 添加到任意 Web 应用
了解将 Flutter 视图嵌入 Web 内容的不同方式。
Flutter 视图与 Web 内容可以不同方式组合成 Web 应用。请根据用例选择以下之一:
-
Flutter 视图控制整页(full page mode,全页模式)
-
将 Flutter 视图添加到现有 Web 应用(embedded mode,嵌入模式)
全页模式
#在全页模式下,Flutter Web 应用控制整个浏览器窗口,渲染时完全覆盖其视口。
这是新 Flutter Web 项目的默认嵌入模式,无需额外配置。
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="flutter_bootstrap.js" defer></script>
</body>
</html>
当 Flutter Web 启动时未引用 multiViewEnabled 或 hostElement 时,将使用全页模式。
要了解 flutter_bootstrap.js 文件的更多信息,请参阅 自定义应用初始化。
iframe 嵌入
#
通过 iframe 嵌入 Flutter Web 应用时,建议使用全页模式。嵌入 iframe 的页面可按需设置其大小与位置,Flutter 将完全填充它。
<iframe src="https://url-to-your-flutter/index.html"></iframe>
要了解 iframe 的优缺点,请参阅 MDN 上的 Inline Frame element
文档。
嵌入模式
#Flutter Web 应用也可将内容渲染到另一 Web 应用的任意数量元素(通常是 div)中;这称为「嵌入模式」(或「多视图」)。
在此模式下:
-
Flutter Web 应用可以启动,但在通过
addView添加第一个「视图」之前不会渲染。 -
宿主应用可以向嵌入的 Flutter Web 应用添加或移除视图。
-
添加或移除视图时 Flutter 应用会收到通知,从而相应调整其 widget。
启用多视图模式
#在 initializeEngine 方法中设置 multiViewEnabled: true 以启用多视图模式,如下所示:
{{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.
}
});
从 JS 管理 Flutter 视图
#要添加或移除视图,请使用 runApp 方法返回的 app 对象:
// Adding a view...
let viewId = app.addView({
hostElement: document.querySelector('#some-element'),
});
// Removing viewId...
let viewConfig = app.removeView(viewId);
从 Dart 处理视图变更
#
视图的添加与移除通过 WidgetsBinding 类的 didChangeMetrics method
暴露给 Flutter。
附加到 Flutter 应用的完整视图列表可通过 WidgetsBinding.instance.platformDispatcher.views 迭代器获取。这些视图的类型为 FlutterView。
要在每个 FlutterView 中渲染内容,Flutter 应用需要创建 View widget。View
widget 可在 ViewCollection widget
下分组。
以下示例来自 Multi View Playground,将上述逻辑封装在可用作应用根 widget 的 MultiViewApp widget 中。每个 FlutterView
都会运行 WidgetBuilder function:
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));
}
}
更多信息请参阅 API 文档中的 WidgetsBinding mixin,或开发时使用的
Multi View Playground repo。
在 Dart 中用 runWidget 替换 runApp
#
Flutter 的 runApp function
假定至少有一个可用于渲染的视图(implicitView),但在 Flutter Web 多视图模式下 implicitView 不再存在,因此 runApp
将开始因 Unexpected null value 错误而失败。
在多视图模式下,main.dart 必须改为调用 runWidget function。它不需要
implicitView,只会渲染到已显式添加到应用中的视图。
以下示例使用上文 MultiViewApp,在每个可用的 FlutterView 上渲染 MyApp() widget 的副本:
void main() {
runWidget(
MultiViewApp(
viewBuilder: (BuildContext context) => const MyApp(),
),
);
}
识别视图
#每个 FlutterView 在附加时由 Flutter 分配标识符。该 viewId 可用于唯一标识每个视图、获取其初始配置或决定渲染内容。
已渲染 FlutterView 的 viewId 可通过其 BuildContext 如下获取:
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;
// ...
同样,在 MultiViewApp 的 viewBuilder 方法中,可如下获取 viewId:
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`...
},
)
了解更多请参阅 View.of constructor。
初始视图配置
#Flutter 视图在启动时可从 JS 接收任意初始化数据。值通过 addView 方法的 initialData 属性传递,如下所示:
// Adding a view with initial data...
let viewId = app.addView({
hostElement: someElement,
initialData: {
greeting: 'Hello, world!',
randomValue: Math.floor(Math.random() * 100),
}
});
在 Dart 中,initialData 作为 JSAny 对象可用,可通过 dart:ui_web 库中的顶层 views
属性访问。数据通过当前视图的 viewId 访问,如下所示:
final initialData = ui_web.views.getInitialData(viewId) as YourJsInteropType;
要了解如何定义 YourJsInteropType 类以映射从 JS 传入的 initialData 对象,从而在 Dart 程序中实现类型安全,请参阅 dart.dev 上的
JS Interoperability。
视图约束
#默认情况下,嵌入的 Flutter Web 视图将 hostElement 的大小视为不可变属性,并严格将布局约束在可用空间内。
在 Web 上,元素的固有尺寸常会影响页面布局(例如 img 或 p 标签可使周围内容重排)。
向 Flutter Web 添加视图时,可配置约束以告知 Flutter 视图应如何布局:
// Adding a view with initial data...
let viewId = app.addView({
hostElement: someElement,
viewConstraints: {
maxWidth: 320,
minHeight: 0,
maxHeight: Infinity,
}
});
从 JS 传入的视图约束需与嵌入 Flutter 的 hostElement 的 CSS 样式兼容。例如,若 CSS 为 max-height: 100px
而向 Flutter 传入 maxHeight: Infinity,Flutter 不会试图“修复”这类矛盾常量。
了解更多请参阅 ViewConstraints class
与理解约束。
自定义元素(hostElement)
#
你可以将单视图 Flutter Web 应用嵌入网页的任意 HTML 元素。
要指定 Flutter Web 渲染到哪个元素,请向 _flutter.loader.load 传入带 config 字段的对象,并将 HTMLElement
指定为 hostElement。
_flutter.loader.load({
config: {
hostElement: document.getElementById('flutter_host'),
}
});
要了解其他配置选项,请参阅 自定义 Web 应用初始化。
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-04。查看文档源码 或者 为本页面内容提出建议。