真正完全兼容的 DSBridge for Flutter

DSBridge for Flutter

三端易用的现代跨平台 JavaScript bridge,通过它你可以在 JavaScript 和 Flutter 之间同步或异步的调用彼此的函数.

概述

DSBridge for Flutter 完全兼容 Android 和 iOS DSBridge 的 dsbridge.js。不像其他类似的框架无法实现JavaScript 调用 Dart 并同步返回结果,本框架完整支持同步调用和异步调用。dsbridge_flutter 是首个完整实现了 DSBridge 在原 Android 和 iOS 上的所有功能,因此可以实现将原来通过原生实现的 Webview 业务完全迁移到 Flutter 实现,即一套代码实现APP与H5的Hybrid开发。在现有使用了 dsbridge.js 的 Web 项目中无须修改任何代码即可使用 DSBridge for Flutter。

本框架目前支持Android 和 iOS 平台,即将支持纯鸿蒙平台(OpenHarmony & HarmonyOS Next),敬请期待!

DSBridge for Flutter 基于 Flutter官方的 webview_flutter

目前已发布到官方pub.dev:dsbridge_flutter

特性

  1. Android、iOS、JavaScript 三端易用,轻量且强大、安全且健壮。

  2. 同时支持同步调用和异步调用

  3. 支持以类的方式集中统一管理API

  4. 支持API命名空间

  5. 支持调试模式

  6. 支持 API 存在性检测

  7. 支持进度回调:一次调用,多次返回

  8. 支持 JavaScript 关闭页面事件回调

  9. 支持 JavaScript 模态对话框

安装

  1. 添加依赖

    yml 复制代码
    dependencies:
      ...
      dsbridge_flutter: x.y.z

示例

请参考工程目录下的 example 包。运行 example 工程并查看示例交互。

如果要在你自己的项目中使用 dsBridge :

使用

  1. 新建一个Dart类,实现API

    dart 复制代码
    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必须在register函数中使用registerFunction来注册。

  2. 添加API类实例到DWebViewController

    dart 复制代码
    import 'package:dsbridge_flutter/dsbridge_flutter.dart';
    ...
    late final DWebViewController _controller;
    ...
    _controller.addJavaScriptObject(JsApi(), null);
  3. 在 JavaScript 中调用 Dart API ,并注册一个 JavaScript API 供原生调用.

    • 初始化 dsBridge

      javascript 复制代码
      //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")
    • 调用 Dart API;以及注册一个 JavaScript API 供 Dart 调用.

      javascript 复制代码
      //同步调用
      var str=dsBridge.call("testSyn","testSyn");
      
      //异步调用
      dsBridge.call("testAsyn","testAsyn", function (v) {
        alert(v);
      })
      
      //注册 JavaScript API 
       dsBridge.register('addValue',function(l,r){
           return l+r;
       })
  4. 在 Dart 中调用 JavaScript API

    dart 复制代码
    import 'package:dsbridge_flutter/dsbridge_flutter.dart';
    ...
    late final DWebViewController _controller;
    ...
    _controller.callHandler('addValue', args: [3, 4],
        handler: (retValue) {
      print(retValue.toString());
    });

Dart API 签名

为了兼容Android&iOS,我们约定Dart API 签名,注意,如果API签名不合法,则不会被调用!签名如下:

  1. 同步API.

    any handler(dynamic msg)

    参数必须是 dynamic 类型,并且必须申明(如果不需要参数,申明后不适用即可)。返回值类型没有限制,可以是任意类型。

  2. 异步 API.

    void handler(dynamic arg, CompletionHandler handler)

命名空间

命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,比如在混合应用中。DSBridge支持你通过命名空间将API分类管理,并且命名空间支持多级的,不同级之间只需用'.' 分隔即可。

调试模式

在调试模式时,发生一些错误时,将会以弹窗形式提示,并且Dart API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。

进度回调

通常情况下,调用一个方法结束后会返回一个结果,是一一对应的。但是有时会遇到一次调用需要多次返回的场景,比如在 JavaScript 中调用端上的一个下载文件功能,端上在下载过程中会多次通知 JavaScript 进度, 然后 JavaScript 将进度信息展示在h5页面上,这是一个典型的一次调用,多次返回的场景,如果使用其它 JavaScript bridge, 你将会发现要实现这个功能会比较麻烦,而 DSBridge 本身支持进度回调,你可以非常简单方便的实现一次调用需要多次返回的场景,下面我们实现一个倒计时的例子:

In Dart

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

javascript 复制代码
dsBridge.call("callProgress", function (value) {
    document.getElementById("progress").innerText = value
})

完整的示例代码请参考example工程。

Javascript 对话框

DSBridge 已经实现了 JavaScript 的对话框函数(alert/confirm/prompt),如果你想自定义它们,通过DWebViewController设置相关回调函数即可。DSBridge实现的对话框默认设置是模态的,这会挂起UI线程。

API 列表

Dart API

在 Dart 中我们把实现了供 JavaScript 调用的 API 类的实例称为 Dart API object.

DWebViewController.addJavaScriptObject(JavaScriptNamespaceInterface? object, String? namespace)

Dart API object到DWebViewController,并为它指定一个命名空间。然后,在 JavaScript 中就可以通过bridge.call("namespace.api",...)来调用Dart API object中的原生API了。

如果命名空间是空(null或空字符串), 那么这个添加的Dart API object就没有命名空间。在 JavaScript 通过 bridge.call("api",...)调用。

示例:

In Dart

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(), 'echo');

In JavaScript

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));
})
DWebViewController.removeJavaScriptObject(String namespace)

通过命名空间名称移除相应的Dart API object。

DWebViewController.callHandler(String method, {List? args, OnReturnValue? handler})

调用 JavaScript API。handlerName 为 JavaScript API 的名称,可以包含命名空间;参数以数组传递,args数组中的元素依次对应 JavaScript API的形参; handler 用于接收 JavaScript API 的返回值,注意:handler将在Dart主isolate中被执行

示例:

dart 复制代码
_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.javaScriptCloseWindowListener

当 JavaScript 中调用window.close时,DWebViewController 会触发此监听器,你可以自定义回调进行处理。

Example:

dart 复制代码
controller.javaScriptCloseWindowListener = () {
  print('window.close called');
};
DWebViewController.hasJavaScriptMethod(String handlerName, OnReturnValue existCallback)

检测是否存在指定的 JavaScript API,handlerName可以包含命名空间.

示例:

dart 复制代码
_controller.hasJavaScriptMethod('addValue', (retValue) {
  print(retValue.toString());
});
DWebViewController.dispose()

释放资源。在当前页面处于dispose状态时,你应该显式调用它。

JavaScript API

dsBridge

"dsBridge" 在初始化之后可用 .

dsBridge.call(method,[arg,callback])

同步或异步的调用Dart API。

method: Dart API 名称, 可以包含命名空间。

arg:传递给Dart API 的参数。只能传一个,如果需要多个参数时,可以合并成一个json对象参数。

callback(String returnValue): 处理Dart API的返回结果. 可选参数,只有异步调用时才需要提供.

dsBridge.register(methodName|namespace,function|synApiObject)
dsBridge.registerAsyn(methodName|namespace,function|asynApiObject)

注册同步/异步的 JavaScript API. 这两个方法都有两种调用形式:

  1. 注册一个普通的方法,如:

    In JavaScript

    javascript 复制代码
    dsBridge.register('addValue',function(l,r){
         return l+r;
    })
    dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){
         responseCallback(arg1+" "+arg2+" "+arg3);
    })

    In Dart

    dart 复制代码
    _controller.callHandler('addValue', args: [3, 4],
        handler: (retValue) {
      print(retValue.toString());
    });
    
    _controller.callHandler('append', args: ["I", "love", "you"],
        handler: (retValue) {
      print(retValue.toString());
    });
  2. 注册一个对象,指定一个命名空间:

    In JavaScript

    javascript 复制代码
    //namespace test for synchronous calls
    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")
      }
    })

    因为 JavaScript 并不支持函数重载,所以不能在同一个 JavaScript 对象中定义同名的同步函数和异步函数

    In Dart

    dart 复制代码
    _controller.callHandler('test.test1',
        handler: (retValue) {
      print(retValue.toString());
    });
    
    _controller.callHandler('test1.test1',
        handler: (retValue) {
      print(retValue.toString());
    });
dsBridge.hasNativeMethod(handlerName,[type])

检测Dart中是否存在名为handlerName的API, handlerName 可以包含命名空间.

type: 可选参数,["all"|"syn"|"asyn" ], 默认是 "all".

javascript 复制代码
//检测是否存在一个名为'testAsyn'的API(无论同步还是异步)
dsBridge.hasNativeMethod('testAsyn') 
//检测test命名空间下是否存在一个'testAsyn'的API
dsBridge.hasNativeMethod('test.testAsyn')
// 检测是否存在一个名为"testSyn"的异步API
dsBridge.hasNativeMethod('testSyn','asyn') //false

最后

如果你喜欢DSBridge for Flutter,欢迎点点star和like,以便更多的人知道它, 谢谢 !

相关推荐
江上清风山间明月1 天前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能1 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人2 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen2 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang2 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang2 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1232 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter