Flutter JS Plus
A unified JavaScript runtime for Flutter that works seamlessly across all platforms: Web, Android, iOS, Windows, macOS, and Linux.
Features
- ✅ Cross-platform: Single API works on all Flutter platforms
- ✅ Web Support: Uses browser's native JavaScript engine via
dart:js_interop - ✅ Native Performance: QuickJS on Android/Windows/Linux, JavaScriptCore on iOS/macOS
- ✅ Promise Support: Automatically converts JavaScript Promises to Dart Futures (on web)
- ✅ Type Conversion: Automatic conversion between JavaScript and Dart types
- ✅ Error Handling: Proper exception handling with stack traces
- ✅ Zero Configuration: Works out of the box on all platforms
Installation
Add flutter_js_pro to your pubspec.yaml:
dependencies:
flutter_js_pro: ^0.0.1
Platform-Specific Setup
Android
Set minimum SDK version to 21 or higher in android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 21
}
}
iOS/macOS
No additional setup required. Uses native JavaScriptCore.
Web
No additional setup required. Uses browser's JavaScript engine.
Windows/Linux
No additional setup required. Uses QuickJS.
Quick Start
Basic Usage
import 'package:flutter_js_pro/js_plus.dart';
void main() async {
// Create a JavaScript runtime
final js = JsPlus();
try {
// Evaluate simple expressions
final result = await js.evaluate('1 + 2');
print(result); // 3
// Evaluate with variables
await js.evaluate('const x = 10;');
final sum = await js.evaluate('x + 5');
print(sum); // 15
// Work with objects
final obj = await js.evaluate('''
({ name: "Flutter", version: 3.0 })
''');
print(obj); // {name: Flutter, version: 3.0}
// Work with arrays
final arr = await js.evaluate('[1, 2, 3, 4, 5]');
print(arr); // [1, 2, 3, 4, 5]
} catch (e) {
if (e is JsEvaluationException) {
print('JavaScript error: ${e.message}');
}
} finally {
// Always dispose when done
js.dispose();
}
}
Working with Promises (Web)
On web, JavaScript Promises are automatically converted to Dart Futures:
final js = JsPlus();
// Promise automatically becomes a Future
final result = await js.evaluate('''
Promise.resolve(42)
''');
print(result); // 42
// Async operations
final data = await js.evaluate('''
fetch('https://api.example.com/data')
.then(response => response.json())
''');
print(data); // The JSON data
js.dispose();
Error Handling
final js = JsPlus();
try {
await js.evaluate('throw new Error("Something went wrong")');
} on JsEvaluationException catch (e) {
print('Error: ${e.message}');
print('Stack: ${e.stackTrace}');
} finally {
js.dispose();
}
Type Conversion
The runtime automatically converts JavaScript types to Dart types:
final js = JsPlus();
// Numbers
final num = await js.evaluate('42');
print(num is int); // true
// Strings
final str = await js.evaluate('"Hello"');
print(str is String); // true
// Booleans
final bool = await js.evaluate('true');
print(bool is bool); // true
// Objects → Maps
final obj = await js.evaluate('{a: 1, b: 2}');
print(obj is Map); // true
print(obj['a']); // 1
// Arrays → Lists
final arr = await js.evaluate('[1, 2, 3]');
print(arr is List); // true
print(arr[0]); // 1
js.dispose();
For advanced type conversion, use the JsTypeConversion utilities:
import 'package:flutter_js_pro/js_plus.dart';
final js = JsPlus();
final jsArray = await js.evaluate('[1, 2, 3]');
final list = JsTypeConversion.jsArrayToList(jsArray);
print(list); // [1, 2, 3]
final jsObj = await js.evaluate('{a: 1}');
final map = JsTypeConversion.jsObjectToMap(jsObj);
print(map); // {a: 1}
js.dispose();
Platform-Specific Behavior
Web
On Flutter Web, flutter_js_pro uses the browser's native JavaScript engine through dart:js_interop. This means:
- Full access to browser APIs (
document,window,localStorage, etc.) - DOM manipulation capabilities
- Native Promise support (automatically converted to Futures)
- No additional JavaScript engine bundled (uses browser's engine)
Example - DOM Manipulation (Web only):
final js = JsPlus();
// Create HTML elements
await js.evaluate('''
const div = document.createElement('div');
div.id = 'my-element';
div.innerHTML = 'Hello from JavaScript!';
document.body.appendChild(div);
''');
// Modify elements
await js.evaluate('''
const element = document.getElementById('my-element');
element.style.color = 'blue';
''');
js.dispose();
Native Platforms
On native platforms (Android, iOS, Windows, macOS, Linux), flutter_js_pro uses:
- Android: QuickJS (default) or JavaScriptCore (if forced)
- iOS/macOS: JavaScriptCore (native to Apple platforms)
- Windows/Linux: QuickJS
Native Configuration:
// Use JavaScriptCore on Android instead of QuickJS
final js = JsPlus(
forceJavascriptCoreOnAndroid: true,
);
// Configure stack size for QuickJS
final js = JsPlus(
extraArgs: {'stackSize': 2 * 1024 * 1024}, // 2MB
);
js.dispose();
Advanced Usage
Sharing Data Between Evaluations
final js = JsPlus();
// Set a variable
await js.evaluate('const myVar = "Hello";');
// Use it in another evaluation
final result = await js.evaluate('myVar + " World"');
print(result); // "Hello World"
js.dispose();
Complex JavaScript Code
final js = JsPlus();
final result = await js.evaluate('''
(function() {
function factorial(n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
return factorial(5);
})()
''');
print(result); // 120
js.dispose();
API Reference
JsPlus
Main class for JavaScript evaluation.
Constructor
JsPlus({
bool forceJavascriptCoreOnAndroid = false,
Map<String, dynamic>? extraArgs,
})
forceJavascriptCoreOnAndroid: Use JavaScriptCore instead of QuickJS on AndroidextraArgs: Additional arguments for native runtime (e.g.,{'stackSize': 1024 * 1024})
Methods
Future<dynamic> evaluate(String code): Evaluate JavaScript code and return the resultvoid dispose(): Dispose of the runtime and free resources
JsEvaluationException
Exception thrown when JavaScript evaluation fails.
String message: Error messageString? stackTrace: JavaScript stack trace (if available)
JsTypeConversion
Utility class for type conversion between JavaScript and Dart.
List<dynamic>? jsArrayToList(dynamic value): Convert JS array to Dart ListMap<String, dynamic>? jsObjectToMap(dynamic value): Convert JS object to Dart MapString listToJsArray(List<dynamic> list): Convert Dart List to JS array stringString mapToJsObject(Map<String, dynamic> map): Convert Dart Map to JS object string
Limitations
Web
- Uses
eval()internally, which may be blocked by Content Security Policy (CSP) in some environments - JavaScript code runs in the global scope (be careful with variable pollution)
- Some browser APIs may not be available depending on the context
Native
- QuickJS has some limitations compared to full JavaScript engines (see QuickJS documentation)
- JavaScriptCore on iOS/macOS is the full engine, but has different performance characteristics than QuickJS
Safety Notes
Using eval() on Web
This package uses JavaScript's eval() function on web platforms. While this is necessary for dynamic code evaluation, be aware that:
- Security: Only evaluate code from trusted sources
- CSP: Some Content Security Policies may block
eval() - Performance:
eval()can be slower than pre-compiled code
Memory Management
Always call dispose() when you're done with a JsPlus instance, especially on native platforms:
final js = JsPlus();
try {
// Use js...
} finally {
js.dispose(); // Important!
}
Examples
See the example/ directory for complete examples including:
- Basic JavaScript evaluation
- Promise handling
- Type conversion
- Error handling
- DOM manipulation (web)
Migration from Legacy API
If you're using the legacy getJavascriptRuntime() API, you can migrate to the new JsPlus API:
Before:
final runtime = getJavascriptRuntime();
final result = runtime.evaluate('1 + 2');
print(result.stringResult);
runtime.dispose();
After:
final js = JsPlus();
final result = await js.evaluate('1 + 2');
print(result); // Direct value, no need for .stringResult
js.dispose();
The legacy API is still available for backward compatibility.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is open source under the MIT license.
Acknowledgments
- QuickJS by Fabrice Bellard and Charlie Gordon
- JavaScriptCore bindings from flutter_jscore
- QuickJS FFI bindings inspired by flutter_qjs
Libraries
- extensions/fetch
- extensions/handle_promises
- extensions/xhr
- flutter_js
- javascript_runtime
- javascript_runtime_stub
- javascriptcore/binding/js_base
- javascriptcore/binding/js_context_ref
- javascriptcore/binding/js_global_context_ref
- javascriptcore/binding/js_object_ref
- javascriptcore/binding/js_string_ref
- javascriptcore/binding/js_typed_array
- javascriptcore/binding/js_value_ref
- javascriptcore/binding/jsc_ffi
- javascriptcore/flutter_jscore
- javascriptcore/jscore/js_class
- javascriptcore/jscore/js_context
- javascriptcore/jscore/js_context_group
- javascriptcore/jscore/js_object
- javascriptcore/jscore/js_property_name_accumulator
- javascriptcore/jscore/js_property_name_array
- javascriptcore/jscore/js_string
- javascriptcore/jscore/js_value
- javascriptcore/jscore_bindings
- javascriptcore/jscore_runtime
- js_eval_result
- js_plus
- Main entry point for flutter_js_pro package
- quickjs-sync-server/quickjs_oasis_jsbridge
- quickjs/ffi
- quickjs/qjs_typedefs
- quickjs/quickjs_runtime
- quickjs/quickjs_runtime2
- quickjs/utf8_null_terminated
- web/web_runtime
- web_runtime_stub