【问题标题】:Use JS library in Flutter在 Flutter 中使用 JS 库
【发布时间】: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 这不是问题代码。发布已经为我工作的东西的完整代码是没有意义的。

标签: dart flutter


【解决方案1】:

我最终通过使用rmtmckenzie 在此answer 中建议的平台渠道解决了这个问题。

我下载了 JS 文件并将其分别保存到 Android 和 iOS 的 android/app/src/main/res/raw/ether.jsios/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,最好的办法是使用 AndroidiOS 特定的库来做实际的以太坊工作,并通过 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);
      }
    });
  });
}

【讨论】:

    猜你喜欢
    • 2020-06-02
    • 1970-01-01
    • 2021-05-14
    • 2018-11-27
    • 2022-11-25
    • 2019-05-15
    • 2021-09-17
    • 2021-02-06
    • 1970-01-01
    相关资源
    最近更新 更多