# flujs **Repository Path**: flujs_project/flujs ## Basic Information - **Project Name**: flujs - **Description**: flujs: js engine binding for flutter - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2024-12-28 - **Last Updated**: 2025-07-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # flujs [简体中文](./README_zh.md) js engine binding for flutter, powered by JavaScriptCore and QuickJS. | js engine | android | iOS | windows | linux | darwin | web | | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | QuickJS | ✅ | ✅ | ✅ | ✅ | ✅ | 🔳 | | JavaScriptCore | 🔳 | ✅ | 🔳 | 🔳 | ✅ | 🔳 | > ✅ : Support > 🔳 : InProgress ## features - easily to use: js call native or native call js. - supply xhr、promis、fetch、setTimeout、setInterval、console intrinsic functions. - friendly dart api. - support multiple JSRuntime concurrently running. ## Getting Started add `flujs`、`flujs_jsc` or `flujs_qjs` dependences ```sh flutter pub add flujs flujs_qjs flujs_jsc ``` 1、get JSFRuntime ```dart import 'package:flujs_qjs/qjs.dart' as qjs; import 'package:flujs_jsc/jsc.dart' as jsc; var rt = qjs.getJSFRuntime(); ``` > get different js engine implement as needed. > > the jsc engine's advantage is mature > > the qjs engine's advantage is small size 2、get JSFContext ```dart var ctx = rt.newContext(); ``` 3、load intrinsic functions. ```dart ctx.loadExtension(); ``` > requires an explicit call. you can also load different built-in functions on demand. 4、execute js code ```dart var res = ctx.eval('''eval('2+2')'''); print(res.toString()); ``` > currently only support gloabl variable、global function. 5、js call native ```dart // common function ctx.addInterface('run', (args) { debugPrint('[native] $args'); return 1; }); // async function ctx.addInterface('runAsync1', (args) async { var completer = Completer(); Timer(const Duration(seconds: 1), () { completer.complete(1); }); return completer.future; }); ctx.addInterface('runAsync2', (args) async { var r = await asyn2(1); return r; }); Future asyn2(int delaySecs) async { var completer = Completer(); Timer(const Duration(seconds: delaySecs), () { completer.complete(1); }); return completer.future; } var res = ctx.eval(''' run('from js', 2); runAsync1().then(() => runAsync2()).then(console.log); 3; '''); debugPrint('runAsync: $res'); ``` 6、native call js ```js function injs(time) { console.log('injs -> ', time); } ``` ```dart var injs = ctx.globalObject.getProperty('injs'); assert(injs.isFunction()); injs.callAsFunction(arguments: [ JSFValueFactory.makeNumber(ctx, id) ]); ``` > get global variable、global function in js with `context.globalContext`. > > if you want to isolate environment, just create different JSFContext 7、custom extension ```dart class CustomExtension extends JSFExtension {} ``` > the main purpose of extensions is to inject custom logic in advance in the context. > built-in xhr relies on the http library, and supports custom HttpClient so that other capabilities such as http cache, rpc, etc. can be implemented ```dart class DioAdapter extends http.BaseClient { final dio = Dio(); @override Future send(http.BaseRequest request) async { final response = await dio.requestUri( request.url, options: Options(headers: request.headers), ); return http.StreamedResponse(response.data, response.statusCode ?? 400); } } class LoggingInterceptor implements InterceptorContract { @override Future interceptRequest({ required http.BaseRequest request, }) { print('[Logging] 😯 request: make cache or something.'); return Future.value(request); } @override Future interceptResponse({ required http.BaseResponse response, }) { print('[Logging] 😊 response: make cache or something.'); return Future.value(response); } @override Future shouldInterceptRequest() { return Future.value(true); } @override Future shouldInterceptResponse() { return Future.value(true); } } var ctx = rt.newContext()..loadExtension(xhr: client == null); final client = InterceptedClient.build( interceptors: [LoggingInterceptor()], ); final client = DioAdapter(); final client = HttpPackageAdapter(); ctx.extension ?.chain(XhrExtension(ctx, client_: client).chain(FetchExtension(ctx))) .load(); ``` > Attention: must invoke `load` method after `extension?.chain`, that will load the extension code into `globalObject`. ## Debug you can use your own compiled qjs engine for debugging by manually setting dynamic library files or custom environment variables ```sh # Android replace libqjs.so in the /flujs_qjs/android/src/main/jniLibs directory or specify flujs.qjs_baseUrl=https://xxxx in the gradle.properties configuration file or environment variables, and libqjs.so package it as qjs_android_[abi].tar.gz # iOS & macOS copy libqjs.dylib to the /flujs_qjs/[ios|macos]/Frameworks/ directory or set QJS_BASEURL=https://xxx, libqjs.dylib to be packaged as qjs_iphoneos.tar.gz/qjs_macosx.tar.gz # windows & linux set QJS_BASEURL=https://xxx, libqjs.dll/libqjs.so to package as qjs_linux_abi.tar.gz/qjs_mingw_abi.tar.gz ``` specify the environment variable QJS_FORCE_DOWNLOAD=true to force the download from the remote end and not read the local cache dynamic library quickJS Cross-Compilation Reference Documentation [XMAKE QuickJS-ng Cross-Compilation Practices] (https://blog.dang8080.cn/2024/11/17/09/) ## Work In Progress - [x] esm module support. - [x] arrayBuffer/Blob support.fetch multiple response type support. ## Known Bugs - [x] toDart isArray cause crash. - [x] QJS xhr addInterface decode HttpHeader failed. - [x] QJS JSContext.create(rt,intrinsic: true) will cause crash. ## The Giants - [flutter-js](https://github.com/abner/flutter_js) - [flutter-jscore](https://github.com/xuelongqy/flutter_jscore) - [flutter-qjs](https://github.com/ekibun/flutter_qjs) - [quickjs-android](https://github.com/seven332/quickjs-android) - [jsx](https://github.com/RxReader/jsc) - [minnet-quickjs](https://github.com/khanhas/minnet-quickjs)