Skip to main content

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 配置。Object
onEntrypointLoaded引擎准备好初始化时调用的函数。接收一个 engineInitializer 对象作为其唯一参数。Function

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

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

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

示例:基于 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();
  }
});