Flutter JavaScript执行插件flutter_js_stable的使用
Flutter JavaScript执行插件flutter_js_stable的使用
Flutter JS Stable
这是flutter_js的一个稳定版本,其中包含了针对Android QuickJS运行时的修复。
Flutter JS 插件
这是一个用于在Flutter中使用的JavaScript引擎。目前在Android上通过Dart ffi使用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模式验证)、moment(日期时间解析和操作),这些库可以直接在移动设备上运行,无需PlatformChannels。
在iOS上,此库依赖于iOS SDK提供的原生JavaScriptCore。在Android上,它使用了Fabrice Bellard和Charlie Gordon令人惊叹且小巧的JavaScript引擎QuickJS(https://bellard.org/quickjs/)。
要在iOS上调试JS代码,你需要设置javascriptRuntime.setInspectable(true);
并传递sourceUrl给evaluate(例如:sourceUrl: ‘script.js’)。
在Android上,你也可以使用JavaScriptCore。你只需要添加一个Android依赖implementation "com.github.fast-development.android-js-runtimes:fastdev-jsruntimes-jsc:0.3.4"
并传递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的策略,主要是为了防止苹果商店拒绝,因为苹果商店规定:
Apps may contain or run code that is not embedded in the binary (e.g. HTML5-based games, bots, etc.), as long as code distribution isn’t the main purpose of the app。 你的应用必须使用WebKit和JavaScript Core来运行第三方软件,并不应试图扩展或暴露本机平台API给第三方软件。
因此,我们避免在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绑定的实现,并且还构造了一个包装API,该API向Dart提供了统一的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('点击大黄色JS按钮使用flutter_js插件评估以下表达式')),
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('错误: ${e.details}');
}
},
),
),
);
}
}
如何从JavaScript调用Dart
你可以在JavascriptRuntime
对象上添加一个通道以接收来自JavaScript引擎的调用:
在Dart端:
javascriptRuntime.onMessage('someChannelName', (dynamic args) {
print(args);
});
现在,如果您的JavaScript代码调用sendMessage('someChannelName', JSON.stringify([1,2,3]);
,上述作为第二个参数提供的Dart函数将被调用,并带有包含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上使用JavaScriptCore。我们从这个惊人的包中得到了JavaScriptCore绑定。但是,默认情况下我们在Android上提供QuickJS作为JavaScript运行时,因为它提供了更小的体积。此外,我们的库还支持ConsoleLog、SetTimeout、Xhr、Fetch和Promises,以便在脚本评估中使用,并允许您的Flutter应用通过onMessage
函数提供dartFunctions作为通道,供JavaScript代码内部调用。
https://pub.dev/packages/flutter_qjs
一个令人惊叹的包,通过Dart ffi实现了QuickJS JavaScript引擎。唯一不同的是它也在iOS设备上使用QuickJS,我们认为这可能会导致苹果商店审核过程出现问题。在flutter_js 0.4.0版本中,我们增加了对桌面的支持并改进了Dart/Js集成,我们从flutter_qjs源代码中借用了C函数绑定和Dart/JS转换及集成。我们只是对其进行调整以支持xhr、fetch,并保持flutter_js通过JavascriptRuntime
类提供的相同接口。
小APK大小
根据Flutter文档,一个简单的Hello World Flutter应用大约有4.2 MB或4.6 MB。
|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
我们刚刚添加了一个使用amazing js库的示例,该库允许将最先进的JSON模式验证功能带入Flutter世界。我们可以在https://github.com/abner/flutter_js/blob/master/example/lib/ajv_example.dart 中看到Ajv示例。
以下是我们在示例应用中添加的屏幕:
iOS
[ios_form]
[ios_ajv_result]
Android
[android_form]
[android_ajv_result]
MacOS
解决 Command Line Tool - Error - xcrun: error: unable to find utility “xcodebuild”,not a developer tool or in PATH
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
在Catalina中使用XCode 12时,我需要安装ruby 2.7.2才能安装cocoapods(也需要Flutter在iOS上运行)。所以我安装了brew和rbenv。
要启用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>
Windows 和 Linux
C包装库托管在GitHub仓库:https://github.com/abner/quickjs-c-bridge
我们只是分离了代码以允许构建它,而在这个仓库中只有已发布的共享库,因此每个使用flutter_js的应用程序不需要每次都重新编译它。
QuickJS Android 共享库
库包装器,包括QuickJS和JavaScriptCore,也在一个单独的仓库中编译:https://github.com/fast-development/android-js-runtimes
随着库被编译并发布到jitpack,通过flutter_js使用这些包装器的应用程序不需要使用Android NDK编译共享库。
单元测试JavaScript评估
我们可以通过桌面平台(Windows、Linux 和 MacOS)使用flutter_js单元测试JavaScript表达式的评估。
对于Windows和Linux,您需要先构建您的应用程序桌面可执行文件:flutter build -d windows
或 flutter build -d linux
。
在Windows上,首次构建您的应用程序后,至少需要将路径build\windows\runner\Debug
(绝对路径)添加到您的环境变量中。
在PowerShell中,只需运行 $env:path += ";${pwd}\build\windows\runner\Debug"
。现在您可以在添加了\build\windows\runner\Debug
到路径的命令行会话中运行测试。
对于Linux,您需要导出一个名为LIBQUICKJSC_TEST_PATH
的环境变量,指向build/linux/debug/bundle/lib/libquickjs_c_bridge_plugin.so
。例如:export LIBQUICKJSC_TEST_PATH="$PWD/build/linux/debug/bundle/lib/libquickjs_c_bridge_plugin.so"
要将测试集成到Visual Studio Code中,您需要在.vscode/launch.json
文件中设置一个启动器,以便您可以填写Windows上的PATH
和Linux上的LIBQUICKJSC_TEST_PATH
:
{
"version": "0.2.0",
"configurations": [
{
"name": "test-with-flutterjs",
"type": "dart",
"program": "test/flutter_js_test.dart",
"windows": {
"env": {
"PATH": "${env:Path};${workspaceFolder}\\example\\build\\windows\\runner\\Debug"
}
},
"linux": {
"env": {
"LIBQUICKJSC_TEST_PATH": "${workspaceFolder}/example/build/linux/debug/bundle/lib/libquickjs_c_bridge_plugin.so"
}
},
"request": "launch"
}
]
}
更多关于Flutter JavaScript执行插件flutter_js_stable的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter JavaScript执行插件flutter_js_stable的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
flutter_js_stable
是一个 Flutter 插件,允许你在 Flutter 应用中执行 JavaScript 代码。它基于 quickjs
引擎,提供了一个轻量级且高效的 JavaScript 执行环境。以下是如何在 Flutter 项目中使用 flutter_js_stable
插件的详细步骤。
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 flutter_js_stable
插件的依赖:
dependencies:
flutter:
sdk: flutter
flutter_js_stable: ^0.3.0 # 请使用最新版本
然后,运行 flutter pub get
来获取依赖。
2. 初始化 JavaScript 引擎
在你的 Dart 代码中,首先需要初始化 JavaScript 引擎。你可以在 initState
或其他合适的地方进行初始化。
import 'package:flutter_js_stable/flutter_js_stable.dart';
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late FlutterJs flutterJs;
[@override](/user/override)
void initState() {
super.initState();
flutterJs = FlutterJs();
}
[@override](/user/override)
void dispose() {
flutterJs.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter JS Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
runJavaScript();
},
child: Text('Run JavaScript'),
),
),
),
);
}
void runJavaScript() {
String jsCode = '''
function add(a, b) {
return a + b;
}
add(2, 3);
''';
var result = flutterJs.eval(jsCode);
print('JavaScript Result: $result'); // 输出: JavaScript Result: 5
}
}
3. 执行 JavaScript 代码
在上面的代码中,runJavaScript
函数演示了如何执行一段 JavaScript 代码。flutterJs.eval(jsCode)
方法用于执行 JavaScript 代码并返回结果。
4. 处理 JavaScript 错误
在实际应用中,JavaScript 代码可能会抛出错误。你可以通过捕获异常来处理这些错误。
void runJavaScript() {
String jsCode = '''
function add(a, b) {
return a + b;
}
add(2, '3'); // 这里会返回字符串 '23'
''';
try {
var result = flutterJs.eval(jsCode);
print('JavaScript Result: $result'); // 输出: JavaScript Result: 23
} catch (e) {
print('JavaScript Error: $e');
}
}
5. 与 Dart 交互
你可以通过 flutterJs.callFunction
方法来调用 JavaScript 中的函数,并传递参数。
void runJavaScript() {
String jsCode = '''
function add(a, b) {
return a + b;
}
''';
flutterJs.eval(jsCode); // 先定义函数
var result = flutterJs.callFunction('add', [2, 3]);
print('JavaScript Result: $result'); // 输出: JavaScript Result: 5
}
6. 释放资源
在使用完 JavaScript 引擎后,记得调用 flutterJs.dispose()
来释放资源。
[@override](/user/override)
void dispose() {
flutterJs.dispose();
super.dispose();
}