dart - 在 Flutter 中使用 JS 库

我正在尝试使用 Ether JS在我的 Flutter 应用程序中。我知道它没有得到直接支持,甚至现有的实现也没有得到很好的记录。

有什么方法可以在我的 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 的菜鸟。任何帮助将不胜感激。

最佳答案

我最终通过使用 rmtmckenzie 建议的平台 channel 解决了这个问题。在这answer .

我下载了 JS 文件并将其保存到 android/app/src/main/res/raw/ether.jsios/runner/ether.js 以供分别是 Android 和 iOS。

安装依赖项

安卓

添加 LiquidCore作为应用程序级别的依赖项 build.gradle

implementation 'com.github.LiquidPlayer:LiquidCore:0.5.0'

iOS

对于 iOS,我使用了 JavaScriptCore这是 SDK 的一部分。

平台 channel

在我的例子中,我需要创建一个基于我传入 JS 函数的助记符(查找 BIP39)的钱包。为此,我创建了一个平台 channel ,它将助记符(基本上是字符串类型)作为参数传递,完成后将返回一个 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库交互并将结果返回到平台 channel 的实际操作。

@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
}

https://stackoverflow.com/questions/52330102/

相关文章:

image - Flutter - 将图像上传到 Firebase 存储

dart - 如何将 showModalBottomSheet 设置为全高?

flutter - 如何在 flutter 中更改 textField 的下划线颜色?

android - Flutter Android Studio 中缺少设备文件资源管理器选项

flutter - 在 Flutter 的嵌套 Navigator 结构中,如何获取特定的 Navi

dart - Flutter 嵌套小部件回调/异步问题

multithreading - 如何在 Flutter 中在后台运行任务?

flutter - 如何知道小部件在视口(viewport)中是否可见?

android - Flutter 使用 ProGuard 和 Firebase Auth 构建崩溃

firebase - Flutter Admob AppID 使用 Android 还是 iOS?