flutterweb_在FlutterWeb中使⽤JavaScript代码
flutter web
When building cross-platform applications that can run on web, mobile and desktop, we often don’t need to access the underlying platform — we are either showing static data or we are communicating with a backend server to post and display newdata.
在构建可在Web,移动和桌⾯上运⾏的跨平台应⽤程序时,我们通常不需要访问底层平台-我们要么显⽰静态数据,要么与后端服务器通信以发布和显⽰新数据。
However, there are cases in which we need to retrieve some information about the platform — device’s specifications, location or hardware such as camera and sensors — or even access a specific native library or API, which requires us to communicate with the platform’s native code.
但是,在某些情况下,我们需要检索有关平台的某些信息(设备的规格,位置或硬件,例如摄像头和传感器),甚⾄访问特定的本机库或API,这需要我们与平台的本机代码进⾏通信。
With Android and iOS we use to send and receive messages from the native platform, and we can use to display native UI views on Flutter. But how we can do it in Flutter Web?
在Android和iOS上,我们使⽤从本机平台发送和接收消息,并且可以使⽤在Flutter上显⽰本机UI视图。 但是我们如何在Flutter Web中做到这⼀点?
JavaScript和Dart (JavaScript and Dart)
Before Flutter was revealed to the public, Dart was used to build web apps (as you can see in this article from 2013 — by ), which was possible due to two of its features:
在Flutter公开露⾯之前,Dart曾被⽤来构建Web应⽤程序(如您在2013年的这篇⽂章中所见-),这是有可能的,这是由于其两个功能:
The ability to compile Dart code to JavaScript;
编译能⼒Dart代码转换为JavaScript ;
JavaScript-Dart via the js package that allows us to call Dart code in JavaScript.
JavaScript-Dart 通过js包允许我们⽤JavaScript调⽤Dart代码。
This means that we have a direct way to communicate with JavaScript without the need of PlatformChannels
这意味着我们⽆需使⽤PlatformChannels就可以直接与JavaScript通信
To use the js package, we must declare in a separate file the functions in JavaScript that we need to call with the help of the @JS annotation.
要使⽤js包,我们必须在单独的⽂件中声明需要在@JS注释的帮助下调⽤的JavaScript函数。
As an example, let’s look at the documentation and see how it is possible to use in Dart:
例如,让我们看⼀下⽂档,看看如何在Dart中使⽤ :
@JS()
library stringify;
import 'package:js/js.dart';
// Calls invoke JavaScript `JSON.stringify(obj)`.
@JS('JSON.stringify')
external String stringify(Object obj);
The library statement has the @JS() annotation, followed by the js.dart import statement.
library语句具有@JS()批注,后跟js.dart导⼊语句。
@JS is used to specify which function we are calling, in this case, JSON.stringify . Note that the declared function has no body and is external, which you can read more about in this .
@JS⽤于指定我们要调⽤的函数,在这种情况下为JSON.stringify 。 请注意,声明的函数没有主体,并且是external ,您可以在此了解更多信息。
在Flutter上显⽰警报窗⼝ (Showing an Alert Window on Flutter)
One of the reasons we might need to interact with JavaScript is to use a core feature, such as displaying an Alert or window. These interactions are used to warn a user before an action — be it navigating away from the website or doing a critical action such as deleting data.
我们可能需要与JavaScript交互的原因之⼀是使⽤⼀项核⼼功能,例如显⽰“警报”或“窗⼝。 这些交互⽤于在操作之前警告⽤户-⽆论是从⽹站导航还是执⾏重要操作(例如删除数据)。
Confirm Dialog
js购物车结算代码
确认对话框
To use it, we must need to use the following code in JavaScript:
要使⽤它,我们必须在JavaScript使⽤以下代码:
confirm("Are you sure");
In out Flutter app, we may want to give it another name, such as showConfirm, and for that we can use the @JS annotation as in the documentation’s example:
在Flutter应⽤程序中,我们可能想给它起另⼀个名字,例如showConfirm ,为此,我们可以使⽤@JS注释,如⽂档⽰例中所⽰:
@JS()
library javascript_bundler;
import 'package:js/js.dart';
@JS('confirm')
external void showConfirm(String text);
Then, we can simply call this method on our Flutter app:
然后,我们可以在Flutter应⽤程序中简单地调⽤此⽅法:
void _showConfirmButton() {
showConfirm("Hello, this is a confirm");
}
And sure enough a new confirm window is going to be displayed to the user:并确定将向⽤户显⽰⼀个新的确认窗⼝:
Confirm dialog in Flutter app
在Flutter应⽤中确认对话框
多平台问题(The Problem of Multiplatform)
As stated previously, when using Flutter we can deploy our apps to multiple platforms. However, in the previous example we are using a js library to run JavaScript code, something that both iOS and Android does not support.
如前所述,在使⽤Flutter时,我们可以将应⽤程序部署到多个平台。 但是,在前⾯的⽰例中,我们使⽤js库运⾏JavaScript代码,iOS和Android均不⽀持。
If we try to run our app in an iOS device, we will get the following compilation error:
如果尝试在iOS设备上运⾏我们的应⽤,则会收到以下编译错误:
Launching lib/main.dart on iPhone 11 in
Running
Xcode build done.                                            8.5s
Failed to build iOS app
Error output from Xcode build:
** BUILD FAILED **
Xcode's output:
lib/main.dart:2:66: Error: Unexpected token ';'.
import 'package:flutter_dart_js/bundler/javascript_bundler.dart';;
^
../../../.pub-cache/hosted//js-0.6.2/lib/js.dart:8:1: Error: Not found: 'dart:js'
export 'dart:js' show allowInterop, allowInteropCaptureThis;
^
Unhandled exception:
FileSystemException(uri=org-dartlang-untranslatable-uri:dart%3Ajs; message=StandardFileSystem only supports file:* and data:* URIs)
#0      ityForUri (package:front_end/src/api_prototype/standard_file_system.dart:33:7)
#1      asFileUri (package:vm/kernel_front_end.dart:659:37)
#2      writeDepfile (package:vm/kernel_front_end.dart:853:21)
<asynchronous suspension>
#3      FrontendCompilerpile (package:frontend_server/frontend_server.dart:574:15)
<asynchronous suspension>
#4      _FlutterFrontendCompilerpile (package:flutter_frontend_server/server.dart:43:22)
#5      starter (package:flutter_frontend_server/server.dart:182:27)
#6      main (file:///b/s/w/ir/cache/builder/src/flutter/flutter_frontend_server/bin/starter.dart:9:30)
#7      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:299:32)
#8      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
Command PhaseScriptExecution failed with a nonzero exit code
note: Using new build system
note: Planning build
note: Constructing build description
This means that when compiling for other platforms, we cannot use code code that has references to JavaScript or dart:html. One way to go around this is to use conditional importing, which you can read more in s Article: .
这意味着在为其他平台进⾏编译时,我们不能使⽤引⽤了JavaScript或dart:html 。 解决此问题的⼀种⽅法是使⽤条件导⼊,您可以在的⽂章: 条件导⼊中阅读更多内容。
We start by creating a stub class in which we declare all the methods we will be using, in this case the showConfirm method:
我们⾸先创建⼀个stub类,在其中声明要使⽤的所有⽅法,在本例中为showConfirm⽅法:
void showConfirm(String message) {
throw UnimplementedError();
}
Then, we can have one file per platform where we specify the implementation of the showConfirm method. For our purpose, we are going to leave this as it is, since it enables us to compile our app to all platforms, if needed we will specify each implementation in the future.
然后,我们可以在每个平台上有⼀个⽂件,在其中指定showConfirm⽅法的实现。 就我们的⽬的⽽⾔,我们将保持原样,因为它使我们能够将我们的应⽤程序编译到所有平台,如果需要,我们将在以后指定每个实现。
Finally, we change our simple imports to conditional imports at the top of the file where we use the showConfirm method:
最后,我们在使⽤showConfirm⽅法的⽂件顶部将简单导⼊更改为条件导⼊:
import 'package:flutter_dart_js/bundler/stub_bundler.dart'
if (dart.library.js) 'package:flutter_dart_js/bundler/javascript_bundler.dart';
In summary, this is importing the stub_bundler.dart by default, however, if it verifies that the platform can access to
dart.library.js(such as in Flutter Web), it will import the second file, where we have our code to show a JavaScript confirm window. This will enable us to compile our code to any platform with the only caveat being that when we call that function on any platform other than web, we will get an UnimplementedError.
总之,这是默认情况下导⼊的stub_bundler.dart ,但是,如果它验证平台可以访问dart.library.js (例如在Flutter Web中),它将导⼊第⼆个⽂件,在此⽂件中显⽰⼀个JavaScript确认窗⼝。 这将使我们能够将代码编译到任何平台,唯⼀的警告是,当我们在除Web之外的任何平台上调⽤该函数时,都会收到
UnimplementedError 。
添加⾃定义JavaScript库 (Adding Custom JavaScript Libraries)
While developing our apps, there might be the need to add a specific feature that has been already in the platform that we are developing for such as Google Maps.
在开发应⽤程序时,可能需要添加我们正在开发的平台中已经存在的特定功能,例如Google Maps。
Google Maps libraries are widely available for Android, iOS and Web, and instead of recreating it in Flutter, Google’s approach was to use these native libraries and display them in Flutter (you can see the implementation in the official GitHub repository).
Google Maps库⼴泛⽤于Android,iOS和Web,⽽不是在Flutter中重新创建,⽽Google的⽅法是使⽤这些本机库并在Flutter中显⽰它们(您可以在官⽅ GitHub存储库中查看实现)。
In the case of web, we don’t have a Pod file or gradle to add our dependencies, so we must add them to our web folder, either declaring them on the index.html file directly or by adding a new .js file in the structure.
对于⽹络,我们没有Pod⽂件或gradle来添加我们的依赖项,因此我们必须将它们添加到我们的web⽂件夹中,或者直接在index.html⽂件中声明它们,或者通过在其中添加新的.js⽂件结构。
To demonstrate this, we are going to add a simpler library — , which allows us to manipulate our Strings like converting text to camel case.
为了演⽰这⼀点,我们将添加⼀个更简单的库 ,该库使我们能够像将⽂本转换为驼峰⼤⼩写⼀样操纵字符串。
We start by downloading the normal, not minified version, of file so that we can take a peek into the class names and comments. Next, we add it to our project, in a js folder inside web