flutter_qjs
A quickjs engine for flutter.
Feature
This plugin is a simple js engine for flutter using the quickjs project with dart:ffi. Plugin currently supports all the platforms except web!
Event loop of FlutterQjs should be implemented by calling FlutterQjs.dispatch().
ES6 module with import function is supported and can be managed in dart with setModuleHandler.
A global function convert is presented to invoke dart function. Data conversion between dart and js are implemented as follow:
| dart | js |
|---|---|
| Bool | boolean |
| Int | number |
| Double | number |
| String | string |
| Uint8List | ArrayBuffer |
| List | Array |
| Map | Object |
| JSFunction | function(....args) |
| Future | Promise |
notice: function can only be sent from js to dart. Promise return by evaluate will be automatically tracked and return the resolved data.
Getting Started
- Create a
FlutterQjsobject. Calldispatchto dispatch event loop.
final engine = FlutterQjs();
await engine.dispatch();
- Call
setMethodHandlerto implementdartinteraction. For example, you can useDioto implement http in js:
await engine.setMethodHandler((String method, List arg) {
switch (method) {
case "http":
return Dio().get(arg[0]).then((response) => response.data);
default:
throw Exception("No such method");
}
});
and in javascript, call convert function to get data, make sure the second memeber is a list:
convert("http", ["http://example.com/"]);
- Call
setModuleHandlerto resolve the js module.
important: I cannot find a way to convert the sync ffi callback into an async function. So the assets files received by async function rootBundle.loadString cannot be used in this version. I will appreciate it if you can provide me a solution to make ModuleHandler async.
await engine.setModuleHandler((String module) {
if(module == "hello") return "export default (name) => `hello \${name}!`;";
throw Exception("Module Not found");
});
and in javascript, call import function to get module:
import("hello").then(({default: greet}) => greet("world"));
- Use
evaluateto run js script:
try {
print(await engine.evaluate(code ?? '', "<eval>"));
} catch (e) {
print(e.toString());
}
- Method
recreatecan destroy quickjs runtime that can be recreated again if you callevaluate,recreatcan be used to reset the module cache. Callcloseto stopdispatchwhen you do not need it.
This example contains a complete demonstration on how to use this plugin.
For Mac & IOS developer
I am new to Xcode and iOS developing, and I cannot find a better way to support both simulators and real devices without combining the binary frameworks. To reduce build size, change the s.vendored_frameworks in ios/flutter_qjs.podspec to the specific framework.
For simulator, use:
s.vendored_frameworks = `build/Debug-iphonesimulator/ffiquickjs.framework`
For real device, use:
s.vendored_frameworks = `build/Debug-iphoneos/ffiquickjs.framework`
Two additional notes:
-
quickjs built with
releaseconfig has bug in resolvingPromise. Please let me know if you know the solution. -
ios/make.shlimit the build architectures to avoid combine conflicts. Change themake.shto support another architectures.