在 Flutter 中使用 JS 库
Posted
技术标签:
【中文标题】在 Flutter 中使用 JS 库【英文标题】:Use JS library in Flutter 【发布时间】:2019-02-19 03:45:28 【问题描述】:我正在尝试在我的 Flutter 应用程序中使用 Ether JS。我知道它没有得到直接支持,甚至现有的实现也没有得到很好的记录。
有什么方法可以在我的 android 和 ios 的 Flutter 应用程序中使用这个库?也欢迎任何其他替代建议。
我尝试过js.dart,但不知道如何使用它。我什至不确定这是否是这种情况下的正确选择。
我也试过Flutter WebView Plugin。
plugin.evaljavascript(
'function add(a,b)return a+b;add(2,3);'
).then((s)
print(s);
此函数正确地返回 5
作为响应。但是我不明白如何像这样使用 EtherJS 库。
我是 Flutter、Dart 和 JS 的菜鸟。任何帮助将不胜感激。
【问题讨论】:
plugin.evalJavascript( 'function add(a,b)return a+b;add(2,3);' ).then((s) print(s); 那不适用于我请添加完整代码谢谢:) @Mahmoudsalaheldiensaber 这不是问题代码。发布已经为我工作的东西的完整代码是没有意义的。 【参考方案1】:我最终通过使用rmtmckenzie 在此answer 中建议的平台渠道解决了这个问题。
我下载了 JS 文件并将其分别保存到 Android 和 iOS 的 android/app/src/main/res/raw/ether.js
和 ios/runner/ether.js
。
安装依赖项
安卓
将LiquidCore添加为应用级别build.gradle
的依赖项
implementation 'com.github.LiquidPlayer:LiquidCore:0.5.0'
iOS
对于 iOS,我使用了 JavaScriptCore,它是 SDK 的一部分。
平台频道
在我的例子中,我需要创建一个基于我传入 JS 函数的助记符(查找 BIP39)的钱包。为此,我创建了一个平台通道,它将助记符(基本上是字符串类型)作为参数传递,完成后将返回一个 JSON 对象。
Future<dynamic> getWalletFromMnemonic(@required String mnemonic)
return platform.invokeMethod('getWalletFromMnemonic', [mnemonic]);
Android 实现 (Java)
在MainActivity.java
中在这一行之后添加这个
GeneratedPluginRegistrant.registerWith(this);
String CHANNEL = "UNIQUE_CHANNEL_NAME";
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler()
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result)
if (methodCall.method.equals("getWalletFromMnemonic"))
ArrayList<Object> args = (ArrayList<Object>) methodCall.arguments;
String mnemonic = (String) args.get(0);
JSObject walletFromMnemonic = getWalletFromMnemonic(mnemonic);
if (walletFromMnemonic == null)
result.error("Could not create", "Wallet generation failed", null);
return;
String privateKey = walletFromMnemonic.property("privateKey").toString();
String address = walletFromMnemonic.property("address").toString();
HashMap<String, String> map = new HashMap<>();
map.put("privateKey", privateKey);
map.put("address", address);
JSONObject obj = new JSONObject(map);
result.success(obj.toString());
else
result.notImplemented();
);
声明以下方法,这些方法执行与 JS 库交互并将结果返回到平台通道的实际操作。
@Nullable
@VisibleForTesting
private JSObject getWalletFromMnemonic(String mnemonic)
JSContext jsContext = getJsContext(getEther());
JSObject wallet = getWalletObject(jsContext);
if (wallet == null)
return null;
if (!wallet.hasProperty("fromMnemonic"))
return null;
JSFunction walletFunction = wallet.property("fromMnemonic").toObject().toFunction();
return walletFunction.call(null, mnemonic).toObject();
@Nullable
@VisibleForTesting
private JSObject getWalletObject(JSContext context)
JSObject jsEthers = context.property("ethers").toObject();
if (jsEthers.hasProperty("Wallet"))
return jsEthers.property("Wallet").toObject();
return null;
@VisibleForTesting
String getEther()
String s = "";
InputStream is = getResources().openRawResource(R.raw.ether);
try
s = IOUtils.toString(is);
catch (IOException e)
s = null;
e.printStackTrace();
finally
IOUtils.closeQuietly(is);
return s;
@VisibleForTesting
JSContext getJsContext(String code)
JSContext context = new JSContext();
context.evaluateScript(code);
return context;
iOS 实现 (Swift)
在override application
方法内的AppDelegate.swift
中添加以下行。
final let methodChannelName: String = "UNIQUE_CHANNEL_NAME"
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel.init(name: methodChannelName, binaryMessenger: controller)
methodChannel.setMethodCallHandler(
(call: FlutterMethodCall, result: FlutterResult)-> Void in
if call.method == "getWalletFromMnemonic"
guard let mnemonic = call.arguments as? [String] else
return
if let wallet = self.getWalletFromMnemonic(mnemonic: mnemonic[0])
result(wallet)
else
result("Invalid")
)
添加与 JavaScriptCore 交互的逻辑。
private func getWalletFromMnemonic(mnemonic: String) -> Dictionary<String, String>?
let PRIVATE_KEY = "privateKey"
let ADDRESS = "address"
guard let jsContext = self.initialiseJS(jsFileName: "ether") else return nil
guard let etherObject = jsContext.objectForKeyedSubscript("ethers") else return nil
guard let walletObject = etherObject.objectForKeyedSubscript("Wallet") else return nil
guard let walletFromMnemonicObject = walletObject.objectForKeyedSubscript("fromMnemonic") else
return nil
guard let wallet = walletFromMnemonicObject.call(withArguments: [mnemonic]) else return nil
guard let privateKey = wallet.forProperty(PRIVATE_KEY)?.toString() else return nil
guard let address = wallet.forProperty(ADDRESS)?.toString() else return nil
var walletDictionary = Dictionary<String, String>()
walletDictionary[ADDRESS] = address
walletDictionary[PRIVATE_KEY] = privateKey
return walletDictionary
private func initialiseJS(jsFileName: String) -> JSContext?
let jsContext = JSContext()
guard let jsSourcePath = Bundle.main.path(forResource: jsFileName, ofType: "js") else
return nil
do
let jsSourceContents = try String(contentsOfFile: jsSourcePath)
jsContext!.evaluateScript(jsSourceContents)
return jsContext!
catch
print(error.localizedDescription)
return nil
【讨论】:
截至今天,pub pub.dev/packages/flutter_liquidcore 上提供了一个液体核心插件 web 怎么样?我们如何在 Flutter web 中也使用它? @ajil - 您是否长期继续使用这种方法?生产成功了吗? 有人得到这个作品吗?可以发一下github吗?【参考方案2】:老实说,如果您是 Flutter、Dart 和 JS 的新手,除非您愿意投入大量时间,否则您会遇到一些麻烦。它确实取决于你想用 Ether JS 库做什么,但一般来说,你很难将它与颤振集成。有一个Ethereum package,但它的范围似乎比你一直在看的 ether.js 库要窄得多——它似乎主要集中在与 RPC api 的通信而不是处理钱包等。
如果你死心塌地使用 Flutter,最好的办法是使用 Android 和 iOS 特定的库来做实际的以太坊工作,并通过 Platform Channels 与你的 dart 代码中的公共 api 进行通信.这可能是一项重大任务,具体取决于您需要公开多少 API,尤其是对于刚接触 flutter/dart 以及可能还接触 android/ios 开发的人。这将比在 webview 中运行的 javascript 来回通信性能要高得多,而且实际上可能也更容易编码,因为 Flutter 目前并没有任何好的机制来调用 js 代码。
还有另一种选择 - 完全使用不同的客户端 UI 框架。 React native 可能会做你需要的一切,并且具有使用 Javascript 的优势,因此它很可能很容易集成 Ether.js 库,尽管我不能保证它实际上会完全支持 ether.js 库(它的运行时可能例如,没有必要的加密扩展)。
【讨论】:
平台渠道看起来像我在这种情况下所需要的。我在原生 Android 和 iOS 方面有一些开发经验。这应该是我可以使用的。 啊,好吧,当您不了解某人的背景时,总是很难知道该建议什么。一旦您习惯了它的工作方式,平台通道(以及方法通道)就会相对简单。祝你好运!【参考方案3】: Future<String> loadJS(String name) async
var givenJS = rootBundle.loadString('assets/$name.js');
return givenJS.then((String js)
flutterWebViewPlugin.onStateChanged.listen((viewState) async
if (viewState.type == WebViewState.finishLoad)
flutterWebViewPlugin.evalJavascript(js);
);
);
【讨论】:
以上是关于在 Flutter 中使用 JS 库的主要内容,如果未能解决你的问题,请参考以下文章
在flutter中使用Chopper网络库和flutter_bloc库
使用 Platform Channels 在 Flutter 中调用原生库