Bridge Communication
Understand how EasyCord routes messages between JavaScript and native Dart environments without serialization overhead.
Overview
The EasyCord Bridge facilitates bidirectional communication between the web frontend and the native mobile environment. It establishes a persistent, high-performance message port when the application loads.
Use the Bridge to:
- Execute native Dart functions from JavaScript.
- Pass JSON payloads between environments.
- Transfer raw binary data (e.g. images) seamlessly via zero-copy
ArrayBufferpayloads. - Stream continuous sensor data from native hardware to the web UI.
Example
The most common operation is invoking a native Dart method from JavaScript and awaiting its response.
try {
const result = await window.easycord.call('getBatteryLevel', {
detailed: true
});
console.log(`Battery level: ${result.level}%`);
} catch (error) {
console.error('Failed to read battery:', error.message);
}How It Works
When EasyCord.initialize() runs in Dart, it injects the window.easycord JavaScript object into the WebView.
When JavaScript calls window.easycord.call(), the payload routes through a native JavaScriptChannel. Dart processes the request, checks permissions against easycord_permissions.yaml, executes the registered handler, and returns a stringified JSON response directly into the pending JavaScript Promise.
Advanced Features
Multi-WebView Concurrency
EasyCord's native bridge is designed to support multiple simultaneous WebViews within the same application (for example, a primary application window and a Picture-in-Picture background worker).
The native EasyCordBridge dynamically routes messages, events, and native streams (like GPS tracking) to the exact EasyCordWebView instance that requested them. Each webview is securely isolated using a generated webviewOffset injected at initialization, ensuring streamId tokens remain globally unique across your enterprise application.
Strict JS Payload Validation
To prevent malformed bridge payloads from reaching the native layer and causing unhandled Dart casting exceptions, EasyCord injects a strict runtime schema validator directly into the JavaScript SDK.
Core plugins (like easycord_fs and easycord_sqlite) validate JavaScript argument types (strings, integers, booleans) locally inside the WebView before passing the serialization boundary. If validation fails, the native bridge is bypassed entirely, and your JavaScript promise immediately rejects with a clear, readable validation error.
Next Steps
API
call()
Executes a registered native Dart method.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| method | string | Yes | The exact string name registered in Dart. |
| payload | object | No | JSON-serializable arguments to pass to the native handler. |
Returns
Promise<any> containing the JSON response from Dart.
Throws
MethodNotAllowedError: The method is not whitelisted ineasycord_permissions.yaml.MethodNotFoundError: The method is not registered viaEasyCordBridge.register().NativeExecutionError: The Dart handler threw an exception during execution.
stream()
Subscribes to a continuous stream of events from Dart.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | The exact stream name registered in Dart. |
| callback | function | Yes | Function invoked every time Dart emits a new event payload. |
Returns
function: An unsubscribe function. Calling it terminates the native stream.
Examples
Registering a Dart Handler
Before JavaScript can call a method, you must register it in Dart.
import 'package:easycord/easycord.dart';
import 'package:flutter/material.dart';
void main() {
EasyCordBridge.register('getBatteryLevel', (Map<String, dynamic> payload) async {
final bool detailed = payload['detailed'] ?? false;
final int batteryLevel = await getNativeBatteryLevel();
return {
'level': batteryLevel,
'status': detailed ? 'charging' : 'unknown'
};
});
runApp(const MyApp());
}Streaming Native Data to JavaScript
Register a stream in Dart to continuously push data.
import 'package:easycord/easycord.dart';
void registerLocationStream() {
EasyCordBridge.createStream('locationUpdates', (emit) {
final subscription = NativeGPS.getPositionStream().listen((position) {
emit({ 'lat': position.latitude, 'lng': position.longitude });
});
return () => subscription.cancel();
});
}Subscribe to the stream in JavaScript.
const unsubscribe = window.easycord.stream('locationUpdates', (position) => {
console.log(`Lat: ${position.lat}, Lng: ${position.lng}`);
});
// Stop receiving updates and cancel the Dart stream
unsubscribe();Developers often call a method in JavaScript without registering it in Dart or adding it to easycord_permissions.yaml.
Cause: Security restrictions prevent arbitrary method execution.
Solution:
- Verify
EasyCordBridge.register('myMethod', ...)runs in Dart. - Add
myMethodto the allowed list ineasycord_permissions.yaml.