2024-07-10 fair release4.0生成插件

FairCommonPlugin插件
所有需要到的动态化逻辑,都用FairCommonPlugin这个插件来进行扩展, 完成从js到dart的到调用和回调。

如何开发插件

1、编写FairCommonPluginMixin

fair_toast_plugin.dart
将此文件放到 lib/src/plugin/目录下面:

mixin FairHttpPlugin implements FairCommonPluginMixin {
  Future<dynamic> http(dynamic map) => request(map, _run);

  Future<Map?> _run(Map requestMap) async {
    // implements http here.
    final method = requestMap['method'];
    final url = requestMap['url'];
    Response<String>? result;
    switch (method) {
      case 'GET':
        result = await _get(url);
        break;
      case 'POST':
        result = await _post(url);
        break;
      default:
    }
    if (result != null) {
      return {
        'data': result.data == null ? '' : jsonDecode(result.data!),
        'statusCode': result.statusCode,
      };
    }
    return null;
  }

  static Future<Response<String>> _post(String path,
      {Map<String, String>? queryParameters}) async {
    var resp =
        await _getDio().post<String>(path, queryParameters: queryParameters);
    return Future.value(resp);
  }

  static Future<Response<String>> _get(String path,
      {Map<String, String>? queryParameters}) async {
    var resp =
        await _getDio().get<String>(path, queryParameters: queryParameters);
    return Future.value(resp);
  }

  static Dio? _dio;

  static Dio _getDio() {
    _dio ??= Dio();
    return _dio!;
  }
}

注意这的位置,一定要是lib/src目录下,且实现implements FairCommonPluginMixin

2、执行脚本 生成js和将方法注册

输入命令:

dart run bin/fair_common_plugin.dart

得到产物:
产物1:/assets/plugin/fair_common_plugin.js
产物2:/lib/src/plugin/fair_common_plugin.dart

这样就完成了:
生成FairCommonPlugin().xxx() js方法
生成class FairCommonPlugin extends IFairPlugin在这个插件getRegisterMethods方法表中完成了注册

3、demo中调用

导入插件:

import 'package:example/src/plugin/fair_common_plugin.dart';
import 'package:fair/fair.dart';

在页面中调用逻辑动态化插件:

///build方法中调用:
build(){ 
 ///省略代码
    Container(
                alignment: Alignment.center,
                child: ElevatedButton(
                    onPressed: commonHttp,
                    child: Text('网络请求-基于FairCommonPlugin')),
              ),
 ///省略代码
}

///调用
  commonHttp() {
    FairCommonPlugin().http({
      'method': 'GET',
      'url':
          'https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3b8ae7a4e0884b4d75b8094f6c83cd8c_list_page_data.json',
      'callback': (dynamic result) {
        if (result != null) {
          var statusCode = result['statusCode'];
          if (statusCode == 200) {
            var list = result['data']['data'];
            list.forEach((item) {
              var icon = item['icon'];
              print('icon = $icon');
            });
          }
        }
      }
    });
  }

注意:一定需要使用package://包名导入,不要使用 相对路径方式导入'../src/plugin/fair_common_plugin.dart';

这种是正常的:


image.png

使用相对路径会使第二个参数是[1]就,导致js报错


image.png

fair js调用dart流程解析:

调用顺序:

0、业务调用FairCommonPlugin().xxx方法

FairCommonPlugin().http(convertObjectLiteralToSetOrMap({
                            ['method']: 'GET',
                            ['url']: 'https://wos2.58cdn.com.cn/DeFazYxWvDti/frsupload/3b8ae7a4e0884b4d75b8094f6c83cd8c_list_page_data.json',
                            ['callback']: function dummy(result) {
                                if (result != null) {
                                    let statusCode = result.__op_idx__('statusCode');
                                    if (statusCode == 200) {
                                        let list = result.__op_idx__('data').__op_idx__('data');
                                        list.forEach(function dummy(item) {
                                            let icon = item.__op_idx__('icon');
                                            print(`icon = ${icon}`);
                                        });
                                    }
                                }
                            },
                        }));

1、FairCommonPlugin().xxx实现

/example/assets/plugin/fair_common_plugin.js

// 由 bin/fair_common_plugin.dart 生成
let FairCommonPlugin = function () {
    return {
        futureComplete: function (resp) {
             fairCommonPluginRequest(resp, 'futureComplete');
        },
        http: function (resp) {
             fairCommonPluginRequest(resp, 'http');
        },
        pushNamed: function (resp) {
             fairCommonPluginRequest(resp, 'pushNamed');
        },
        pushFairPath: function (resp) {
             fairCommonPluginRequest(resp, 'pushFairPath');
        },
        pop: function (resp) {
             fairCommonPluginRequest(resp, 'pop');
        },
        toast: function (resp) {
             fairCommonPluginRequest(resp, 'toast');
        }                     
    }
}

2、 fairCommonPluginRequest

Fair/fair/assets/fair_core/fair_common_plugin.js

//用户自定义拓展,需要在fair_basic_config.json中注册
//会在基础js加载之后加载
let _callBack = {};
let _callBackId = 0;
let fairCommonPluginRequest = function (resp, methodName) {
    let respMap = {};
    respMap = mapOrSetToObject(resp);
    let id = 'FairCommonPlugin$' + (++_callBackId);
    let requestParameter = {};
    requestParameter['pageName'] = '#FairKey#';
    // 类名 + 方法名
    requestParameter['className'] = "FairCommonPlugin#" + methodName;
    _callBack[id] = respMap['callback'];
    respMap['callId'] = id;
    // 代码里面有判断 funcName 必填
    requestParameter['funcName'] = 'invokePlugin';
    requestParameter['request'] = respMap;
    let map = JSON.stringify(requestParameter);
    console.log('FairCommonPlugin请求参数:' + map);
    invokeFlutterCommonChannel(map, (resultStr) => {
        console.log('FairCommonPlugin请求结果:' + resultStr);
        let responseMap = JSON.parse(resultStr);
        console.log('FairCommonPlugin请求结果1:' + responseMap);
        let id = responseMap['callId']
        let data = responseMap['response'];
        console.log('FairCommonPlugin请求结果2:' + id);
        // 这两个函数用户拓展的
        if (_callBack[id] === null) {
            return;
        }
        let complete = _callBack[id];
        console.log('FairCommonPlugin请求结果3:' + data);
        // 返回的是 map
        if (data === null) {
            complete(null);
        }
        else {
            complete(convertObjectLiteralToSetOrMap(data));
        }
        _callBack[id] = null
    })
}

3、invokeFlutterCommonChannel

const invokeFlutterCommonChannel = (invokeData, callback) => {
    console.log("invokeData" + invokeData)
    jsInvokeFlutterChannel(invokeData, (resultStr) => {
        console.log('resultStr' + resultStr);
        if (callback) {
            callback(resultStr);
        }
    });
};

4、jsInvokeFlutterChannel

/Fair/fair/ios/Classes/FairDynamicJSPlugin/FairJSBridge.m

        // JS 异步调用 Dart
        _context[FairExecuteDartFunctionAsync] = ^(id receiver, JSValue *callback) {
            FairStrongObject(strongSelf, weakSelf)
            
            NSString *data = [strongSelf convertStringWithData:receiver];
            if ([strongSelf.delegate respondsToSelector:@selector(FairExecuteDartFunctionAsync:callback:)]) {
                [strongSelf.delegate FairExecuteDartFunctionAsync:data callback:callback];
            }
        };

5、BasicMessageChannel 通讯调用dart

com.wuba.fair/common_message_channel

/Fair/fair/lib/src/runtime/fair_message_channel.dart

    _commonChannel!.setMessageHandler((String? message) async {
      print('来自native端的消息:$message');
      //js 异步调用dart中的相关方法
      var data = json.decode(message??'');
      var funcName = data['funcName']?.toString();

      if (funcName == 'invokePlugin') {
        var p = await FairPluginDispatcher.dispatch(message);
        return p;
      }

      _callback?.call(message);
      return 'reply from dart';
    });

6、FairPluginDispatcher 从已注册插件表pluginMap中查找实现

/Fair/fair/lib/src/runtime/plugin/plugin_dispatcher.dart

  static Future<dynamic> dispatch(dynamic msg) async {
    dynamic obj;
    if (msg is Map) {
      obj = msg;
    } else {
      obj = jsonDecode(msg);
    }
    // var args = obj['args'];
    var className = obj['className']?.toString();

    if (className == null || className.isEmpty) {
      return null;
    }

    if (className.contains('#')) {
      className = className.split('#')[0];
    }

    if (pluginMap[className] != null) {
      // var d = await ;
      return pluginMap[className]?.invoke(msg);
    }
  }

7、Function.apply 调用对应的方法

/Fair/fair/lib/src/runtime/plugin/fair_plugin.dart


abstract class IFairPlugin with FairCommonPluginMixin {
  Future<dynamic> invoke(dynamic par) async {
    var resp =
        await Function.apply(getRegisterMethods()[getMethodName(par)]!, [par]);
    return Future.value(resp);
  }

  String getMethodName(dynamic par) {
    dynamic a;
    if (par is Map) {
      a = par;
    } else {
      a = jsonDecode(par);
    }

    var name = a['className']?.toString() ?? '';

    if (name.contains('#')) {
      var list = name.split('#');
      if (list.length >= 2) {
        return list[1];
      }
    }

    return '';
  }

8、 从目标插件中的方法列表中 getRegisterMethods查找对应的实现function

class FairCommonPlugin extends IFairPlugin
    with CompleterPlugin, FairHttpPlugin, FairNavigatorPlugin, FairToastPlugin {
  factory FairCommonPlugin() => _fairCommonPlugin;
  FairCommonPlugin._();
  static final FairCommonPlugin _fairCommonPlugin = FairCommonPlugin._();
  @override
  Map<String, Function> getRegisterMethods() {
    return <String, Function>{
      'futureComplete': futureComplete,
      'http': http,
      'pushNamed': pushNamed,
      'pushFairPath': pushFairPath,
      'pop': pop,
      'toast': toast,
    };
  }
}

9、FairCommonPluginMixin 真正的dart逻辑

/Fair/example/lib/src/plugin/fair_http_plugin.dart


mixin FairHttpPlugin implements FairCommonPluginMixin {
  Future<dynamic> http(dynamic map) => request(map, _run);

  Future<Map?> _run(Map requestMap) async {
    // implements http here.
    final method = requestMap['method'];
    final url = requestMap['url'];
    Response<String>? result;
    switch (method) {
      case 'GET':
        result = await _get(url);
        break;
      case 'POST':
        result = await _post(url);
        break;
      default:
    }
    if (result != null) {
      return {
        'data': result.data == null ? '' : jsonDecode(result.data!),
        'statusCode': result.statusCode,
      };
    }
    return null;
  }

  static Future<Response<String>> _post(String path,
      {Map<String, String>? queryParameters}) async {
    var resp =
        await _getDio().post<String>(path, queryParameters: queryParameters);
    return Future.value(resp);
  }

  static Future<Response<String>> _get(String path,
      {Map<String, String>? queryParameters}) async {
    var resp =
        await _getDio().get<String>(path, queryParameters: queryParameters);
    return Future.value(resp);
  }

  static Dio? _dio;

  static Dio _getDio() {
    _dio ??= Dio();
    return _dio!;
  }
}

10、通用方法request

/Fair/fair/lib/src/runtime/plugin/fair_common_plugin.dart

源码:

mixin FairCommonPluginMixin {
  /// common request method
  Future<dynamic> request(
    dynamic map,
    // do your business logic in this call back
    Future<Map?> Function(Map reqData) run,
  ) async {
    if (map == null) {
      return;
    }
    var req;
    bool isDart;
    if (map is Map) {
      isDart = true;
      req = map;
    } else {
      isDart = false;
      req = jsonDecode(map);
    }
    final pageName = req['pageName'];
    var request = req['request'];
    if (isDart) {
      request = req;
    }

    final callId = request['callId'];

    final completeCallback = request['callback'];

    final response = await run(request);

    // 需要判断发起方的请求是dart端还是js端
    if (isDart) {
      completeCallback?.call(response);
      return Future.value();
    } else {
      final resp = {
        'callId': callId,
        'pageName': pageName,
        'response': response,
      };
      return Future.value(jsonEncode(resp));
    }
  }
}

处理通用的逻辑:

js参数解析:

js业务参数 :request。
通用参数:pageName、request、
request参数:js调用 callId,dart调用callback、业务参数FairCommonPlugin().xx({}///这里传递的参数)

dart回调参数:
'callId': callId,
'pageName': pageName,
'response': response,

处理特有的逻辑:

implements FairCommonPluginMixin中实现:

  Future<Map?> _run(Map requestMap) async {
//只管返回业务的数据。 js dart通讯参数不需要管。
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容