原文作者:medium.com/@thebosz
发布时间:2016年7月20日 - 5分钟阅读
随着我的游戏,我一直在努力创建一套库,以方便使用Dart的Cordova插件。我正在使用新的 "package:js "包。这个新库取代了旧的 "dart:js "库。替换后的库使用起来更加方便,但缺少文档。我将详细介绍一下与Cordova Facebook插件的Javascript互操作所需的所有步骤。
首先要做的是 在你的pubspec.yaml中包含js包,并获取依赖关系。
dependencies:
js:">=0.6.0 <0.7.0"
接下来,为我们的代码创建一个文件,我把我的叫 "facebook.dart"。原始的,我知道。
现在到了有趣的部分,实际构建代码。
所有的互操作代码都需要用"@JS() "注释,而且必须有一个库,所以让我们从这个开始。
@JS()
library facebook;
就这样,Webstorm会因为不知道@JS注解而抓狂。通过导入package:js来解决这个问题。导入必须在库声明之后,所以后面的一行就可以了。
@JS()
library facebook;
import 'package:js/js.dart';
很好,警告消失了。接下来是声明实际的接口的东西。我决定的结构方式是创建一个与Javascript版本相匹配的类,并使方法和属性成为该类的静态成员。这样就可以让调用接口的代码看起来像。
Facebook.login();
这是初始结构。
@JS()
library facebook;
import 'package:js/js.dart';
@JS('facebookConnectPlugin')
class Facebook {
}
请注意,我们再次使用@JS注解,但这次我们传递的是我们想要对话的Javascript对象的名称;在本例中,它是 "facebookConnectPlugin"。
让我们从插件的文档中添加方法名。我们稍后再关注它们的参数。
@JS()
library facebook;
import 'package:js/js.dart';
@JS('facebookConnectPlugin')
class Facebook {
external static login();
external static logout();
external static getLoginStatus();
external static showDialog();
external static api();
external static logEvent();
external static logPurchase();
external static activateApp();
external static appInvite();
}
这里很重要的一点是,这些方法被标记为 "外部"。有一点我觉得很讨厌,如果一个类有@JS注解,所有的方法都必须是外部的。如果所有的方法都必须是外部的,为什么要让程序员明确说明呢?
让我们在方法参数上下功夫。这在回调等方面会有点棘手,但一旦你弄明白了,就不会太糟糕。
@JS('facebookConnectPlugin')
class Facebook {
external static login(List<String> perms, Function success, Function failure);
external static logout(Function success, Function failure);
external static getLoginStatus(Function success, Function failure);
external static showDialog(DialogOptions options, Function success, Function failure);
external static api(String requestPath, List<String> perms, Function success, Function failure);
external static logEvent(String name, Object params, num valueToSum, Function success, Function failure);
external static logPurchase(num value, String currency, Function success, Function failure);
external static activateApp(Function success, Function failure);
external static appInvite(Object options, Function success, Function failure);
}
除了 "DialogOptions",这里没有什么疯狂的东西。其他的都是普通的Dart类型参数。那么,DialogOptions是怎么回事呢?好吧,Javascript期待一个看起来像这样的对象。
{
method: "share",
href: "http://example.com",
caption: "Such caption, very feed.",
description: "Much description",
picture: 'http://example.com/image.png',
share_feedWeb: true // iOS only
}
你会期望这将被表示为一个Dart Map,但地图不能直接翻译成Javascript对象。我不知道为什么,但这是事实。为了绕过这个限制,我们需要创建一个 "匿名 "类。
@JS()
@anonymous
class DialogOptions {
external String get method;
external set method(String v);
external String get href;
external set href(String v);
external String get caption;
external set caption(String v);
external String get description;
external set description(String v);
external String get picture;
external set picture(String v);
external bool get share_feedWeb;
external set share_feedWeb(bool v);
external factory DialogOptions({
String method,
String href,
String caption,
String description,
String picture,
bool share_feedWeb: false
});
}
这个类允许我们这样调用我们的方法。
Facebook.showDialog(new DialogOptions(method: 'share', href: 'http://example.com'), ...);
JS包会帮我们把DialogOptions转换成一个JS对象。知道了这些,那么 "logEvent "和 "appInvite "方法呢?它们都会接受一个对象。不幸的是,看起来这两个方法都允许该参数使用任意对象。我不知道如何去做,所以我把它们作为对象。也许有人能给我指出正确的方向。 最后,我们需要使用回调。所有的方法都使用成功和失败回调。要在JS回调里面使用Dart函数,我们需要用一个特殊的方法来包装它:allowInterop()。这样就可以创建必要的JS来调用我们的Dart。下面是一个使用getLoginStatus的例子。
Facebook.getLoginStatus(allowInterop((var response) {
print('Success login status: ${response}');
}), allowInterop((var response) {
print('Login failure: ${response}');
}));
非常重要的注意:你不能给任何传递给Javascript的函数设置类型,即使你知道它会是什么,dart2js也会崩溃。即使你知道它会是什么,dart2js也会崩溃。我已经在GitHub上提交了一个问题,但它被关闭了,因为Dart2.0修复了这个问题。(关于Dart 2.0的更新请看最后的说明)
这些回调中的响应对象可以像Dart对象一样使用。对于成功,response会是这样的。
{
authResponse: {
userID: "12345678912345",
accessToken: "kgkh3g42kh4g23kh4g2kh34g2kg4k2h4gkh3g4k2h4gk23h4gk2h34gk234gk2h34AndSoOn",
session_Key: true,
expiresIn: "5183738",
sig: "..."
},
status: "connected"
}
你可以在回调中获取用户ID。
var userid = response.authResponse.userID;
然后我们就完成了 我们成功地创建了一个Dart-to-Javascript的互操作库,至少是大部分。你可以在这里看到完整的文件。我喜欢Dart的interop的一个好处是,我们可以对库进行扩展。举个例子,我为亚马逊移动广告做了一个库。他们的Cordova插件有一个不稳定的接口,但我创建了一个单独的库来使用我的interop库,以平衡粗糙的地方。
比较interop的方式来加载和显示一个横幅广告。
AmazonMobileAds.loadAndShowFloatingBannerAd(
allowInterop((var res) {
print("Successful: ${res.booleanValue});
}),
allowInterop((var res) {
print("Failure: ${res});
}), [ad]
);
到更容易理解。
bool wasShown = await AmazonAds.loadAndShowFloatingBannerAd(_amazonBannerAd);
我封装了Javascript interop库,并返回一个Future,而不是使用哑巴回调。相反,它允许调用脚本使用async/await。
下面是一个包装器的例子。
static Future<bool> loadAndShowFloatingBannerAd(Ad ad) {
var completer = new Completer();
AmazonMobileAds.loadAndShowFloatingBannerAd(
allowInterop((var res) {
completer.complete(res.booleanValue);
}),
allowInterop((var res) {
completer.completeError(res);
}), [ad]);
return completer.future;
}
查看GitHub上的亚马逊库,了解更多信息。
Dart 2.0 (更新于6/19/2019)
我提到的关于把类型放在 "allowInterop "函数上的bug似乎在Dart 2.0中得到了修复。我还没有花很多时间去发现其他的东西。
我打算写一个 "Dart 2.0 "版本的指南,但它应该大部分还是可以用的。如果你发现有什么东西不再以同样的方式工作,请随时给我发消息。