Modern cross-platform JavaScript bridge, through which you can invoke each other's functions synchronously or asynchronously between JavaScript and Flutter applications.
Chinese documentation 中文文档
DSBridge-Android:https://github.com/wendux/DSBridge-Android
DSBridge-IOS:https://github.com/wendux/DSBridge-IOS
DSBridge for Flutter is fully compatible with Android and iOS DSBridge's dsbridge.js.
No need to modify any code in your existing web projects which using dsbridge.js.
DSBridge for Flutter is based on Flutter official webview_flutter.
- Supports
Android
,iOS
, andHarmonyOS
, covering all mobile platforms - Easy to use, light and powerful, secure and strong
- Both synchronous and asynchronous calls are supported
- Support API Object, which centrally implements APIs in a Dart Class or a JavaScript object
- Support API namespace
- Support debug mode
- Support the test of whether API exists
- Support Progress Callback: one call, multiple returns
- Support event listener for JavaScript to close the page
- Support Modal popup box for JavaScript
-
Add the dependency
dependencies: ... dsbridge_flutter: x.y.z
See the example
package. run the example
project and to see it in action.
To use dsBridge in your own project:
-
Implement APIs in a Dart class
import 'package:dsbridge_flutter/dsbridge_flutter.dart'; class JsApi extends JavaScriptNamespaceInterface { @override void register() { registerFunction(testSyn); registerFunction(testAsyn); } /// for synchronous invocation String testSyn(dynamic msg) { return "$msg[syn call]"; } /// for asynchronous invocation void testAsyn(dynamic msg, CompletionHandler handler) { handler.complete("$msg [ asyn call]"); } }
- Dart APIs must be registered with registerFunction in register function.
- Note that if you use the
--obfuscate
option to obfuscate Dart code when building your application, you need to annotate the function with@pragma('vm:entry-point')
or use the second parameterfunctionName
ofregisterFunction
to specify the function name.
import 'package:dsbridge_flutter/dsbridge_flutter.dart'; class JsApi extends JavaScriptNamespaceInterface { @override void register() { registerFunction(testSyn, functionName: 'testSyn'); registerFunction(testAsyn); } /// for synchronous invocation String testSyn(dynamic msg) { return "$msg[syn call]"; } /// for asynchronous invocation @pragma('vm:entry-point') void testAsyn(dynamic msg, CompletionHandler handler) { handler.complete("$msg [ asyn call]"); } }
-
Add API object to DWebViewController
import 'package:dsbridge_flutter/dsbridge_flutter.dart'; ... late final DWebViewController _controller; ... _controller.addJavaScriptObject(JsApi());
-
Call Dart API in JavaScript, and register JavaScript API.
-
Init dsBridge
//cdn //<script src="https://unpkg.com/dsbridge@3.1.3/dist/dsbridge.js"> </script> //npm //npm install dsbridge@3.1.3 var dsBridge=require("dsbridge")
-
Call Dart API and register a JavaScript API for Dart invocation.
//Call synchronously var str=dsBridge.call("testSyn","testSyn"); //Call asynchronously dsBridge.call("testAsyn","testAsyn", function (v) { alert(v); }) //Register JavaScript API for Dart dsBridge.register('addValue',function(l,r){ return l+r; })
-
-
Call JavaScript API in Dart
import 'package:dsbridge_flutter/dsbridge_flutter.dart'; ... late final DWebViewController _controller; ... _controller.callHandler('addValue', args: [3, 4], handler: (retValue) { print(retValue.toString()); });
In order to be compatible with Android & iOS & HarmonyOS, we make the following convention on Dart API signature:
-
For synchronous API.
any handler(dynamic msg)
The argument type must be dynamic and must be declared even if not need,and the type of return value is not limited.
-
For asynchronous API.
void handler(dynamic arg, CompletionHandler handler)
Namespaces can help you better manage your APIs, which is very useful in hybrid applications, because these applications have a large number of APIs. DSBridge allows you to classify API with namespace. The namespace can be multilevel, between different levels with '.' division.
In debug mode, some errors will be prompted by a popup dialog , and the exception caused by the dart APIs will not be captured to expose problems.
Normally, when a API is called to end, it returns a result, which corresponds one by one. But sometimes a call need to repeatedly return multiple times, Suppose that on the Flutter side, there is a API to download the file, in the process of downloading, it will send the progress information to JavaScript many times, then JavaScript will display the progress information on the H5 page. Oh...You will find it is difficult to achieve this function. Fortunately, DSBridge supports Progress Callback. You can be very simple and convenient to implement a call that needs to be returned many times. Here's an example of a countdown:
In Dart
void callProgress(dynamic args, CompletionHandler handler) {
var i = 10;
final timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (i == 0) {
timer.cancel();
handler.complete(0);
} else {
handler.setProgressData(i--);
}
});
}
In JavaScript
dsBridge.call("callProgress", function (value) {
document.getElementById("progress").innerText = value
})
For the complete sample code, please refer to the example project.
For JavaScript popup box functions (alert/confirm/prompt), DSBridge has implemented them all by default, if you want to custom them, set the corresponding callback in DWebViewController. The default dialog box implemented by DSBridge is modal. This will block the UI thread.
In Dart, the object that implements the JavaScript interfaces is called Dart API object.
Add the Dart API object with supplied namespace into DWebViewController. The JavaScript can then call Dart APIs with bridge.call("namespace.api",...)
.
If the namespace is null or empty, the Dart API object have no namespace. The JavaScript can call Dart APIs with bridge.call("api",...)
.
Example:
In Dart
class JsEchoApi extends JavaScriptNamespaceInterface {
@override
void register() {
registerFunction(syn);
registerFunction(asyn);
}
dynamic syn(dynamic args) {
return args;
}
void asyn(dynamic args, CompletionHandler handler) {
handler.complete(args);
}
}
//namespace is "echo"
controller.addJavaScriptObject(JsEchoApi(), namespace: 'echo');
In JavaScript
// call echo.syn
var ret=dsBridge.call("echo.syn",{msg:" I am echoSyn call", tag:1})
alert(JSON.stringify(ret))
// call echo.asyn
dsBridge.call("echo.asyn",{msg:" I am echoAsyn call",tag:2},function (ret) {
alert(JSON.stringify(ret));
})
Remove the Dart API object with supplied namespace.
Call the JavaScript API. If a handler
is given, the JavaScript handler can respond. the handlerName
can contain the namespace. The handler will be called in Dart main isolate.
Example:
_controller.callHandler('append', args: ["I", "love", "you"],
handler: (retValue) {
print(retValue.toString());
});
/// call with namespace 'syn', More details to see the Demo project
_controller.callHandler('syn.getInfo', handler: (retValue) {
print(retValue.toString());
});
DWebViewController calls callback
when JavaScript calls window.close
you can provide a callback to add your handler.
Example:
controller.javaScriptCloseWindowListener = () {
print('window.close called');
};
Test whether the handler exist in JavaScript.
Example:
_controller.hasJavaScriptMethod('addValue', (retValue) {
print(retValue.toString());
});
Release resources. You should call it explicitly when page state is dispose.
"dsBridge" is accessed after dsBridge Initialization .
Call Dart api synchronously and asynchronously。
method
: Dart API name, can contain the namespace。
arg
: argument, Only one allowed, if you expect multiple parameters, you can pass them with a json object.
callback(String returnValue)
: callback to handle the result. only asynchronous invocation required.
Register JavaScript synchronous and asynchronous API for Dart invocation. There are two types of invocation
-
Just register a method. For example:
In JavaScript
dsBridge.register('addValue',function(l,r){ return l+r; }) dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){ responseCallback(arg1+" "+arg2+" "+arg3); })
In Dart
_controller.callHandler('addValue', args: [3, 4], handler: (retValue) { print(retValue.toString()); }); _controller.callHandler('append', args: ["I", "love", "you"], handler: (retValue) { print(retValue.toString()); });
-
Register a JavaScript API object with supplied namespace. For example:
In JavaScript
//namespace test for synchronous dsBridge.register("test",{ tag:"test", test1:function(){ return this.tag+"1" }, test2:function(){ return this.tag+"2" } }) //namespace test1 for asynchronous calls dsBridge.registerAsyn("test1",{ tag:"test1", test1:function(responseCallback){ return responseCallback(this.tag+"1") }, test2:function(responseCallback){ return responseCallback(this.tag+"2") } })
Because JavaScript does not support function overloading, it is not possible to define asynchronous function and sync function of the same name。
In Dart
_controller.callHandler('test.test1', handler: (retValue) { print(retValue.toString()); }); _controller.callHandler('test1.test1', handler: (retValue) { print(retValue.toString()); });
Test whether the handler exist in Dart, the handlerName
can contain the namespace.
type
: optional["all"|"syn"|"asyn" ]
, default is "all".
dsBridge.hasNativeMethod('testAsyn')
//test namespace method
dsBridge.hasNativeMethod('test.testAsyn')
// test if exist a asynchronous function that named "testSyn"
dsBridge.hasNativeMethod('testSyn','asyn') //false
If you like DSBridge for Flutter, please click star/like to let more people know it, Thanks!