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"
],
},
]
}
注意:这只在flutter
dev或master分支上支持。
一旦你完成了这些,你就可以开始在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.com向region-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 渲染以获得卓越的性能。
编码愉快!