將 Flutter 新增至任何網頁應用程式
Flutter 視圖和網頁內容可以透過不同的方式組合以產生網頁應用程式。請根據您的使用情境選擇下列其中一種:
全頁模式
#在全頁模式中,Flutter 網頁應用程式會控制整個瀏覽器視窗,並在渲染時完全覆蓋其視口。
這是新 Flutter 網頁專案的預設嵌入模式,不需要額外的設定。
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="flutter_bootstrap.js" defer></script>
</body>
</html>
當啟動 Flutter 網頁時,如果沒有參考 multiViewEnabled
或 hostElement
,它會使用全頁模式。
若要進一步了解 flutter_bootstrap.js
檔案,請查看自訂應用程式初始化。
iframe
嵌入
#當透過 iframe
嵌入 Flutter 網頁應用程式時,建議使用全頁模式。嵌入 iframe
的頁面可以根據需要調整其大小和位置,而 Flutter 會將其完全填滿。
<iframe src="https://url-to-your-flutter/index.html"></iframe>
若要進一步了解 iframe
的優缺點,請查看 MDN 上的內聯框架元素文件。
嵌入模式
#Flutter 網頁應用程式也可以將內容渲染到另一個網頁應用程式的任意數量元素(通常是 div
)中;這稱為「嵌入模式」(或「多視圖」)。
在此模式中:
- Flutter 網頁應用程式可以啟動,但直到使用
addView
新增第一個「視圖」之前都不會渲染。 - 主機應用程式可以從嵌入的 Flutter 網頁應用程式中新增或移除視圖。
- 當新增或移除視圖時,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
方法呈現在 Flutter 中。
附加到 Flutter 應用程式的完整視圖列表可透過 WidgetsBinding.instance.platformDispatcher.views
可迭代物件取得。這些視圖是 FlutterView
類型。
若要將內容渲染到每個 FlutterView
中,您的 Flutter 應用程式需要建立一個 View
widget。View
widget 可以分組在 ViewCollection
widget 下。
以下範例來自多視圖遊樂場,將上述內容封裝在可用作應用程式根 widget 的 MultiViewApp
widget 中。對於每個 FlutterView
,都會執行一個 WidgetBuilder
函式。
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,或開發期間使用的多視圖遊樂場儲存庫。
在 Dart 中將 runApp
替換為 runWidget
#Flutter 的 runApp
函式假設至少有一個可供渲染的視圖 (implicitView
),但在 Flutter 網頁的多視圖模式中,implicitView
不再存在,因此 runApp
會開始失敗並出現 Unexpected null value
錯誤。
在多視圖模式中,您的 main.dart
必須改為呼叫 runWidget
函式。它不需要 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
建構函式 的資訊。
初始檢視設定
#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 互通性。
檢視條件約束
#預設情況下,嵌入的 Flutter 網頁視圖會將其 hostElement
的大小視為不可變的屬性,並將其版面配置緊密限制在可用空間內。
在網頁上,元素的固有大小通常會影響頁面的版面配置(例如可以將內容環繞在它們周圍的 img
或 p
標籤)。
當新增視圖到 Flutter 網頁時,您可以設定限制條件,告知 Flutter 視圖的版面配置方式:
// Adding a view with initial data...
let viewId = app.addView({
hostElement: someElement,
viewConstraints: {
maxWidth: 320,
minHeight: 0,
maxHeight: Infinity,
}
});
從 JS 傳遞的視圖限制條件需要與嵌入 Flutter 的 hostElement
的 CSS 樣式相容。例如,Flutter 不會嘗試「修復」矛盾的常數,例如在 CSS 中傳遞 max-height: 100px
,但傳遞 maxHeight: Infinity
給 Flutter。
若要進一步了解,請查看 ViewConstraints
類別,以及了解限制條件。
自訂元素 (hostElement
)
#在 Flutter 3.10 和 3.24 之間
您可以將單視圖 Flutter 網頁應用程式嵌入到網頁的任何 HTML 元素中。
若要告知 Flutter 網頁要渲染到哪個元素,請將帶有 config
欄位的物件傳遞給 _flutter.loader.load
函式,該欄位會將 HTMLElement
指定為 hostElement
。
_flutter.loader.load({
config: {
hostElement: document.getElementById('flutter_host'),
}
});
若要進一步了解其他設定選項,請查看自訂網頁應用程式初始化。
除非另有說明,否則本網站上的文件反映了 Flutter 的最新穩定版本。此頁面最後更新於 2024-11-12。 檢視原始碼 或 回報問題。