DSBridge for Flutter

在 Android 和 iOS 渠道上做过 Hybrid 开发的同学根本都会知道 DSBridge,该结构现在最受欢迎的 JSBridge 结构之一,为了在 Flutter 侧完成原生 Hybrid 的能力,于是咱们将其适配到了Flutter 渠道。

Flutter 首个真正可商用的 JSBridge 结构(彻底兼容的 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. 增加依赖

    dependencies:
      ...
      dsbridge_flutter: x.y.z
    

示例

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

假如要在你自己的项目中运用 dsBridge :

运用

  1. 新建一个Dart类,完成API

    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

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

    • 初始化 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")
      
    • 调用 Dart API;以及注册一个 JavaScript API 供 Dart 调用.

      
      //同步调用
      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

    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

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
})

完好的示例代码请参阅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

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

// 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中被执行

示例:

_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:

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

检测是否存在指定的 JavaScript API,handlerName能够包括命名空间.

示例:

_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

    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());
    });
    
  2. 注册一个目标,指定一个命名空间:

    In 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

    _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”.

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

最终

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