跳至主要内容

Flutter Web 应用初始化

此页面详细介绍了 Flutter Web 应用的初始化过程以及如何对其进行自定义。

引导启动

#

flutter build web 命令会在构建输出目录(build/web)中生成一个名为 flutter_bootstrap.js 的脚本。此文件包含初始化和运行 Flutter 应用所需的 JavaScript 代码。您可以通过在 Flutter 应用的 web 子目录中的 index.html 文件中放置一个异步脚本标签来使用此脚本。

html
<html>
  <body>
    <script src="flutter_bootstrap.js" async></script>
  </body>
</html>

或者,您可以通过在 index.html 文件中插入模板标记 {{flutter_bootstrap_js}} 来内联 flutter_bootstrap.js 文件的全部内容。

html
<html>
  <body>
    <script>
      {{flutter_bootstrap_js}}
    </script>
  </body>
</html>

在构建步骤期间,当 index.html 文件复制到输出目录(build/web)时,{{flutter_bootstrap_js}} 标记将被 flutter_bootstrap.js 文件的内容替换。

自定义初始化

#

默认情况下,flutter build web 会生成一个 flutter_bootstrap.js 文件,该文件会对 Flutter 应用进行简单的初始化。但是,在某些情况下,您可能需要自定义此初始化过程,例如:

  • 为您的应用设置自定义 Flutter 配置。
  • 更改 Flutter 服务工作者的设置。
  • 编写自定义 JavaScript 代码以在启动过程的不同阶段运行。

要编写自己的自定义引导逻辑,而不是使用构建步骤生成的默认脚本,您可以将 flutter_bootstrap.js 文件放在项目的 web 子目录中,该文件会被复制并使用,而不是构建生成的默认脚本。此文件也是模板化的,您可以插入一些特殊标记,构建步骤在构建时将这些标记替换为将 flutter_bootstrap.js 文件复制到输出目录时。下表列出了构建步骤将在 flutter_bootstrap.jsindex.html 文件中替换的标记。

标记替换为
{{flutter_js}}使 FlutterLoader 对象在 _flutter.loader 全局变量中可用所需的 JavaScript 代码。(有关更多详细信息,请参阅下面的 _flutter.loader.load() API 部分。)
{{flutter_build_config}}一个 JavaScript 语句,它设置构建过程中生成的元数据,这些元数据为 FlutterLoader 提供正确引导应用程序所需的信息。
{{flutter_service_worker_version}}表示服务工作者构建版本的唯一数字,可以将其作为服务工作者配置的一部分传递(请参阅下面的“服务工作者设置”表)。
{{flutter_bootstrap_js}}如上所述,这会将 flutter_bootstrap.js 文件的内容直接内联到 index.html 文件中。请注意,此标记只能用于 index.html,而不能用于 flutter_bootstrap.js 文件本身。

编写自定义引导脚本

#

任何自定义 flutter_bootstrap.js 脚本都需要包含三个组件才能成功启动 Flutter 应用。

  • 一个 {{flutter_js}} 标记,用于使 _flutter.loader 可用。
  • 一个 {{flutter_build_config}} 标记,它向 FlutterLoader 提供有关构建的信息,以便启动应用。
  • _flutter.loader.load() 的调用,这实际上会启动应用。

最基本的 flutter_bootstrap.js 文件看起来像这样。

js
{{flutter_js}}
{{flutter_build_config}}

_flutter.loader.load();

自定义 Flutter 加载器

#

_flutter.loader.load() JavaScript API 可以使用可选参数来调用以自定义初始化行为。

名称描述JS 类型
config应用的 Flutter 配置。对象
onEntrypointLoaded引擎准备好初始化时调用的函数。接收一个 engineInitializer 对象作为其唯一参数。函数

config 参数是一个对象,可以具有以下可选字段。

名称描述Dart 类型
assetBase应用的 assets 目录的基本 URL。当 Flutter 从与实际 Web 应用不同的域或子目录加载时,请添加此项。当您将 Flutter Web 嵌入到另一个应用中,或将其资源部署到 CDN 时,您可能需要此项。字符串
canvasKitBaseUrl下载 canvaskit.wasm 的基本 URL。字符串
canvasKitVariant要下载的 CanvasKit 变体。您的选项包括:

1. auto:为浏览器下载最佳变体。此选项默认为此值。
2. full:下载可在所有浏览器中使用的 CanvasKit 的完整变体。
3. chromium:下载使用 Chromium 兼容 API 的 CanvasKit 的较小变体。警告:除非您计划只使用基于 Chromium 的浏览器,否则不要使用 chromium 选项。
字符串
canvasKitForceCpuOnlytrue 时,强制在 CanvasKit 中仅使用 CPU 进行渲染(引擎不会使用 WebGL)。布尔值
canvasKitMaximumSurfacesCanvasKit 渲染器可以使用的覆盖面的最大数量。双精度浮点数
debugShowSemanticNodes如果 true,Flutter 会在屏幕上可见地渲染语义树(用于调试)。布尔值
entryPointBaseUrlFlutter 应用入口点的基本 URL。默认为“/”。字符串
hostElementFlutter 渲染应用的 HTML 元素。未设置时,Flutter Web 会接管整个页面。HtmlElement
renderer指定当前 Flutter 应用程序的Web 渲染器,可以是 "canvaskit""skwasm"字符串

示例:根据 URL 查询参数自定义 Flutter 配置

#

以下示例显示了一个自定义 flutter_bootstrap.js,它允许用户通过在网站的 URL 中提供 renderer 查询参数(例如 ?renderer=skwasm)来选择渲染器。

js
{{flutter_js}}
{{flutter_build_config}}

const searchParams = new URLSearchParams(window.location.search);
const renderer = searchParams.get('renderer');
const userConfig = renderer ? {'renderer': renderer} : {};
_flutter.loader.load({
  config: userConfig,
});

此脚本评估页面的 URLSearchParams 以确定用户是否传递了 renderer 查询参数,然后更改 Flutter 应用的用户配置。它还会传递服务工作者设置以使用 Flutter 服务工作者,以及服务工作者版本。

onEntrypointLoaded 回调函数

#

您还可以将 onEntrypointLoaded 回调函数传递到 load API 中,以便在初始化过程的不同部分执行自定义逻辑。初始化过程分为以下阶段:

加载入口点脚本
load 函数在服务工作者初始化并且 main.dart.js 入口点已由浏览器下载并运行后,调用 onEntrypointLoaded 回调函数。在开发期间的每次热重启时,Flutter 也会调用 onEntrypointLoaded
初始化 Flutter 引擎
onEntrypointLoaded 回调函数接收一个引擎初始化器对象作为其唯一参数。使用引擎初始化器的 initializeEngine() 函数设置运行时配置,例如 multiViewEnabled: true,并启动 Flutter Web 引擎。
运行应用
initializeEngine() 函数返回一个Promise,该 Promise 解析为一个应用运行器对象。应用运行器有一个名为 runApp() 的方法,该方法用于运行 Flutter 应用。
向应用添加视图(或从应用中移除视图)
runApp() 方法返回一个Flutter 应用对象。在多视图模式下,可以使用 addViewremoveView 方法来管理主机应用中的应用视图。要了解更多信息,请查看嵌入模式

示例:显示进度指示器

#

为了在初始化过程中向应用用户提供反馈,请使用每个阶段提供的钩子来更新 DOM。

js
{{flutter_js}}
{{flutter_build_config}}

const loading = document.createElement('div');
document.body.appendChild(loading);
loading.textContent = "Loading Entrypoint...";
_flutter.loader.load({
  onEntrypointLoaded: async function(engineInitializer) {
    loading.textContent = "Initializing engine...";
    const appRunner = await engineInitializer.initializeEngine();

    loading.textContent = "Running app...";
    await appRunner.runApp();
  }
});