Flutter JS 插件
一个用于 Flutter 的 Javascript 引擎。现在它通过 Dart ffi 在 Android 上使用 QuickJS,在 iOS 上也通过 Dart ffi 使用 JavascriptCore。Javascript 运行时通过 Dart ffi 同步运行。所以现在你可以在你的 Flutter 移动应用(Android、iOS、Windows、Linux 和 MacOS 都支持)中将 Javascript 代码作为原生组件运行。
在之前的版本中,我们只能将求值表达式的结果作为字符串获取。
但现在我们可以用 flutter_js 做更多的事情,比如通过 Dart http 库运行 xhr 和 fetch http 调用。我们也支持 Promises。
有了 flutter_js,Flutter 应用程序可以利用优秀的 Javascript 库,如 ajv(json schema 验证)、moment(DateTime 解析器和操作),在移动设备(Android 和 iOS)上原生运行(无需 PlatformChannels)。
在 iOS 上,此库依赖于 iOS SDK 提供的原生 JavascriptCore。在 Android 上,它使用非常棒且小巧的 Javascript 引擎 QuickJS https://bellard.org/quickjs/(Fabrice Bellard 和 Charlie Gordon 的精彩之作)。
在 Android 上,您也可以使用 JavascriptCore。您只需要添加 Android 依赖项 implementation "com.github.fast-development.android-js-runtimes:fastdev-jsruntimes-jsc:0.1.3",并将 forceJavascriptCoreOnAndroid: true 传递给 getJavascriptRuntime 函数。
在 MacOS 上,使用 OSX 提供的 JavascriptCore。在 Windows 和 Linux 上,使用的引擎是 QuickJS。在 0.4.0 版本中,我们从 flutter_qjs 库中借用了 dart ffi 源代码。flutter_qjs 是一个非常棒的包,它们在构建 Dart 和 JS 之间的良好 ffi 桥梁方面做了出色的工作,还对 quickjs 源代码进行了修改,使其能够在 Windows 上运行。但是,flutter_js 采取使用 JavascriptCore 的方法(主要是在 iOS 上),以避免在 Apple Store 中被拒绝,Apple Store 的规定是,应用程序可以包含或运行未嵌入在二进制文件中的代码(例如,基于 HTML5 的游戏、机器人等),只要代码分发不是应用程序的主要目的。它还规定,您的应用程序必须使用 WebKit 和 JavaScript Core 来运行第三方软件,并且不应尝试将原生平台 API 扩展或暴露给第三方软件;参考:https://developer.apple.com/app-store/review/guidelines/ [ Session 4.7]。因此,我们避免在 iOS 应用程序中使用 quickjs,所以 flutter_js 提供了一个名为 JavascriptRuntime 的抽象,该抽象在 Apple 设备和桌面设备上通过 JavascriptCore 运行,而在 Android、Windows 和 Linux 上通过 QuickJS 运行。
FLutterJS 允许使用 Javascript 来执行 TextFormField 的验证逻辑,我们还可以执行从 Web 应用程序共享的规则引擎或 redux 逻辑。机会非常多。
该项目是开源的,采用 MIT 许可证。
用于通过 dart:ffi 与 JavascriptCore 通信的绑定是我们从 flutter_jscore 包中借用的。
Flutter JS 提供了 QuickJS Dart ffi 绑定的实现,并构建了一个 Dart 的包装器 API,它提供了一个统一的 API 来评估 Javascript,并通过 QuickJS 和 Javascript Core 以统一的方式在 Dart 和 Javascript 之间进行通信。
此库还允许通过 Dart Http 调用在 Javascript 中调用 xhr 和 fetch。我们还提供了允许评估 promises 的实现。

功能
安装
dependencies:
flutter_js: 0.1.0+0
iOS
由于 flutter_js 使用原生 JavascriptCore,因此无需采取任何操作。
Android
在您的 android/app/build.gradle 文件中将最低 Android sdk 版本更改为 21(或更高)。
minSdkVersion 21
发布部署
Android
为发布构建设置 proguard:设置您的 android/app/proguard-rules.pro 文件
包含以下内容。
请记住与其他配置合并,这些配置是您应用程序使用的
其他插件所必需的。
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-keep class de.prosiebensat1digital.** { *; }
还将这些行添加到您的 android -> buildTypes -> release 部分的 android/app/build.gradle 文件中
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
示例
这是一个简单的 Flutter 应用程序,展示了如何在 Flutter 应用中评估 Javascript 代码。
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_js/flutter_js.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _jsResult = '';
JavascriptRuntime flutterJs;
@override
void initState() {
super.initState();
flutterJs = getJavascriptRuntime();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('FlutterJS Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('JS Evaluate Result: $_jsResult\n'),
SizedBox(height: 20,),
Padding(padding: EdgeInsets.all(10), child: Text('Click on the big JS Yellow Button to evaluate the expression bellow using the flutter_js plugin'),),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Math.trunc(Math.random() * 100).toString();", style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic, fontWeight: FontWeight.bold),),
)
],
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.transparent,
child: Image.asset('assets/js.ico'),
onPressed: () async {
try {
JsEvalResult jsResult = flutterJs.evaluate(
"Math.trunc(Math.random() * 100).toString();");
setState(() {
_jsResult = jsResult.stringResult;
});
} on PlatformException catch (e) {
print('ERRO: ${e.details}');
}
},
),
),
);
}
}
如何从 Javascript 调用 Dart
您可以在 JavascriptRuntime 对象上添加一个通道来接收来自 Javascript 引擎的调用。
在 Dart 端
javascriptRuntime.onMessage('someChannelName', (dynamic args) {
print(args);
});
现在,如果您的 Javascript 代码调用 sendMessage('someChannelName', JSON.stringify([1,2,3]);,上面作为第二个参数提供的 Dart 函数将被调用。
List 中包含 1、2、3 作为其元素。
替代方案(以及为什么我们认为我们的库更好)
已经有其他包提供了在 Flutter 项目中评估 Javascript 的替代方案。
https://pub.dev/packages/flutter_liquidcore
好,它基于 https://github.com/LiquidPlayer/LiquidCore
它基于 V8 引擎,所以可执行库非常大(20Mb)。因此最终的应用程序也会很大。
https://pub.dev/packages/interactive_webview
允许在隐藏的 Webview 中评估 Javascript。它不会增加应用程序的大小,但 Webview 意味着整个浏览器仅用于评估 Javascript 代码。所以我们认为嵌入式引擎是一个好得多的解决方案。
https://pub.dev/packages/jsengine
基于 jerryscript,比 quickjs 慢。jsengine 包没有为 iOS 提供实现。
https://pub.dev/packages/flutter_jscore
在 Android 和 IOS 上使用 Javascript Core。我们从这个很棒的包中获取了 JavascriptCore 绑定。但是,默认情况下
我们提供 QuickJS 作为 Android 上的 Javascript 运行时,因为它提供的占位空间更小。另外
我们的库增加了对 ConsoleLog、SetTimeout、Xhr、Fetch 和 Promises 的支持,以便在脚本评估中使用
并允许您的 Flutter 应用通过 onMessage 函数提供 dartFunctions 作为通道,以便在
您的 Javascript 代码中调用。
https://pub.dev/packages/flutter_qjs
一个很棒的包,它通过 Dart ffi 使用 quickjs 实现 Javascript 引擎。
唯一的区别是它也在 iOS 设备上使用 quickjs,我们认为这在通过 Apple Store 审查过程时会很麻烦。在 flutter_js 0.4.0 版本中,我们
增加了对 Desktop 的支持,并改进了 Dart/Js 集成,我们从 flutter_qjs 源代码中借用了 C 函数绑定和 Dart/JS 转换及集成。我们只是对它进行了调整,以支持 xhr、fetch 并保持通过 JavascriptRuntime 类提供的与 flutter_js 相同的接口。
APK 体积小
根据 flutter 文档,一个 hello world Flutter 应用的大小为 4.2Mb 或 4.6Mb。
https://flutterdart.cn/docs/perf/app-size#android
下面您可以看到使用 flutter_js 生成的 示例应用程序 的 APK 大小。
|master ✓| → flutter build apk --split-per-abi
✓ Built build/app/outputs/apk/release/app-armeabi-v7a-release.apk (5.4MB).
✓ Built build/app/outputs/apk/release/app-arm64-v8a-release.apk (5.9MB).
✓ Built build/app/outputs/apk/release/app-x86_64-release.apk (6.1MB).
Ajv
我们刚刚添加了一个使用很棒的 js 库 Ajv 的示例,该库允许将最先进的 json schema 验证功能引入
到 Flutter 世界。您可以在此处查看 Ajv 示例:https://github.com/abner/flutter_js/blob/master/example/lib/ajv_example.dart
请看下面我们添加到示例应用程序中的屏幕截图。
iOS


Android


MACOS
要启用 http 调用,请将此添加到您的文件中
- DebugProfile.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
- Release.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>