使用Firebase的Flutter Web应用程序的8大专业技巧

96 阅读7分钟

Flutter web自2019年12月以来一直处于测试阶段,最近的版本引入了一些性能改进并增强了开发人员的体验(完整的概述在这里)。

越来越多的公司正在使用它来创造良好的用户体验。

而且,如果你已经有一个Flutter移动应用程序,你可以用最小的努力和几乎100%的代码重用将其移植到Web上。

但有几件事你需要注意。因此,在本教程中,我将分享8个顶级技巧,以节省您的Flutter Web项目的时间。

这个列表绝非详尽无遗,主要是基于我在一个项目中使用Flutter web进行生产时遇到的问题(并已解决)。

要开始使用Flutter web,请务必遵循官方文档

现在开始讲技巧

1.使用 --web-enable-expression-evaluation

在VSCode中调试Flutter网络应用,直到最近才得以实现,我们不得不依靠浏览器的开发工具来代替。

--web-enable-expression-evaluation 改变这一点,可以在 文件中启用。.vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "web",
            "program": "lib/main.dart",
            "request": "launch",
            "type": "dart",
            "args": [
                "--web-port=5000",
                "--web-enable-expression-evaluation"
            ],
        },
    ]
}

注意:这只在flutterdevmaster 分支上支持。

一旦你完成了这些,你就可以开始在VSCode中使用断点来调试你的Flutter网络应用。

2.在iPad模拟器上开发

上面的设置使得在网页上调试更容易,但根据我的经验,热重载还是有点不稳定。

通过在iPad模拟器上运行,我得到了优越的开发体验,但屏幕形式与网页上相同。

当然,我还是会不时地在网络上运行,以确保一切都能按预期进行。在测试依赖平台的代码时,这一点尤其重要。

3.为项目启用Firebase主机

用Firebase主机配置和部署任何网站都很简单,而且步骤在这里有详细的记录。

如果我们使用Firebase,我们可以在项目设置页面中添加一个新的Web应用。然后,我们需要将正确的Firebase SDK片段添加到我们的应用中。

如果我们把我们的项目链接到一个Firebase托管网站,一个新的 "自动 "模式就会出现。

项目设置中的Firebase主机设置

如果没有它,我们必须通过CDN手动配置我们的应用程序,把它添加到我们的index.html 文件中。

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "<your-api-key>",
    authDomain: "<your-auth-domain>",
    databaseURL: "<your-database-url>",
    projectId: "<your-project-id>",
    storageBucket: "<your-storage-bucket>",
    messagingSenderId: "<your-messaging-sender-id>",
    appId: "<your-app-id>",
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
</script>

通过使用 "自动 "模式,上面的脚本变得简单多了。

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="/__/firebase/7.14.2/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->

<!-- Initialize Firebase -->
<script src="/__/firebase/init.js"></script>

上面的/__/ 语法指向了一个为Firebase托管网站启用的保留命名空间。在这里阅读更多细节。

4.为不同口味的网站保留多个`index.html`文件

Firebase主机很不错,但你还是想在localhost ,为此你需要使用基于CDN的设置。

与其在index.html 中不断切换Firebase SDK片段,我们可以为不同的环境/口味保留多个副本。这使得我们更容易在localhost、开发和生产中工作。

为了支持这一点,我们可以在web 的旁边添加一个web_flavors 的文件夹。

web/
    index.html
web_flavors/
    localhost/
        index.html
    development/
        index.html
    production/
        index.html

然后,我们可以根据需要将正确的index.html 文件复制到web

对于生产构建,在CI工作流程中添加一个简单的shell脚本就可以了。

# $flavor is an environment variable that is configured on a per-workflow basis
cp web_flavors/$flavor/index.html web/index.html
flutter build web --release

5.处理跨源资源共享(CORS)问题

如果你的网络应用向另一个域(例如,你的云功能托管的地方)发出请求,你在发送请求时将会遇到一些XMLHttpRequest 错误。

要了解为什么会发生这种情况,我们需要了解CORS - 跨源资源共享

这份关于处理 CORS 请求的文件解释了什么是 CORS 以及如何配置它。引述。

跨源资源共享(CORS)是一种让运行在一个域的应用程序访问另一个域的内容的方法,例如,让yourdomain.comregion-project.cloudfunctions.net/yourfunction 。如果CORS没有正确设置,你很可能会得到类似这样的错误。

XMLHttpRequest cannot load https://region-project.cloudfunctions.net/function.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://yourdomain.com' is therefore not allowed access.

这里详细解释了设置CORS的步骤,但这些步骤有点繁琐,特别是当你的API支持多个端点时。

幸运的是,如果你使用Firebase Hosting,处理CORS就会变得很简单,只需要为你的API设置重写规则

重写规则可以在firebase.json 文件中进行配置,这里有解释。

这将解决你部署的Web应用中的任何CORS问题。

但在localhost 中提出请求仍将无法工作,使你的应用程序的部分内容无法使用。

StackOverflow在这里拯救了我们,在"在Chrome中禁用同源策略 "中解释了一些解决方法。我可以通过用这个命令打开Chrome浏览器(在macOS上)来让它工作。

open /Applications/Google\ Chrome.app --args --user-data-dir="/var/tmp/Chrome dev session" --disable-web-security

一旦网络安全被禁用,我们会在Chrome中看到这个横幅。

在禁用网络安全的情况下运行Chrome浏览器

设置后,请求将在localhost

注意:这很有效,但我们仍然需要每次都用--disable-web-security ,打开Chrome。如果你知道有更好的方法,请告诉我。🙏

6.在你的Flutter网络应用中启用复制-粘贴功能

信不信由你,默认情况下,在Flutter网页应用里面是无法复制文本的(在这里继续尝试:文本根本无法选择)。

Flutter提供了一个Clipboard.setData方法,但这在网页上并不适用。幸运的是,这个主题提供了一个变通方法。

// copy_to_clipboard_web.dart
import 'dart:html';

// https://github.com/flutter/flutter/issues/33470#issuecomment-537802636
bool copyToClipboardImpl(String text) {
  final textarea = TextAreaElement();
  document.body.append(textarea);
  textarea.style.border = '0';
  textarea.style.margin = '0';
  textarea.style.padding = '0';
  textarea.style.opacity = '0';
  textarea.style.position = 'absolute';
  textarea.readOnly = true;
  textarea.value = text;
  textarea.select();
  final result = document.execCommand('copy');
  textarea.remove();
  return result;
}

这个方法可行,但我们无法在iOS/Android上构建这段代码,因为那里没有dart:html

不用担心,因为下一个提示显示了如何处理这个问题。

7.在编译时有条件地导入文件

由于dart:html 只在网络上可用,我们需要一种方法来定义并在编译时导入不同的文件。

为了实现这一点,我们需要四个不同的文件。

一个文件(copy_to_clipboard_web.dart )将是网络专用的,包含上面的代码。

第二个文件包含一个非网络实现(在这里使用剪贴板API)。

// copy_to_clipboard_non_web.dart
import 'package:flutter/services.dart';

bool copyToClipboardImpl(String text) {
  Clipboard.setData(ClipboardData(text: text));
  return true;
}

第三个文件将是一个占位符,定义了一个抛出UnsupportedError存根实现。

// copy_to_clipboard_stub.dart
bool copyToClipboardImpl(String text) =>
    throw UnsupportedError('No implementation for copyToClipboardImpl');

最后一个文件将所有的东西放在一起,导入正确的文件,这取决于我们是否要为网络构建。

// copy_to_clipboard.dart
import 'copy_to_clipboard_stub.dart'
    if (dart.library.html) 'copy_to_clipboard_web.dart'
    if (dart.library.io) 'copy_to_clipboard_non_web.dart';

bool copyToClipboard(String text) => copyToClipboardImpl(text);

这里的主要想法是定义三个独立的文件,为copyToClipboardImpl 函数提供三种实现方式。

然后,当我们想复制一些文本时,我们可以调用copyToClipboard

底线:你可以为网络和非网络功能创建不同的源文件,并在编译时使用条件导入import 正确的代码。

这将确保你可以在网络和非网络上构建你的Flutter应用。

但是有没有办法在运行时检查我们是否在Web上运行?

最后一个提示的时间到了。

8.检查我们是否在运行时运行在网络上

我们可以用 Platform类来检查我们在哪个平台上运行。

但是Platform 是在'dart:io' 中定义的,不能在web上使用。

相反,我们可以使用foundation.dart 中的常量kIsWeb

/// A constant that is true if the application was compiled to run on the web.
///
/// This implementation takes advantage of the fact that JavaScript does not
/// support integers. In this environment, Dart's doubles and ints are
/// backed by the same kind of object. Thus a double `0.0` is identical
/// to an integer `0`. This is not true for Dart code running in AOT or on the
/// VM.
const bool kIsWeb = identical(0, 0.0);

这就是如何使用它。

import 'package:flutter/foundation.dart';

if (kIsWeb) {
  print("we're on web");
} else {
  print("we're not on web");
}

总结

Flutter网络是一个令人兴奋的新领域。虽然Flutter在网络上提供一流的体验方面越来越好,但仍有一些怪癖,我希望这些提示会有用。

除了这些提示外,我还推荐阅读这篇文章。

这包括更多关于Flutter网络应用现在默认是渐进式网络应用的信息。以及如何启用 CanvasKit 渲染以获得卓越的性能。

编码愉快!