测试代码
用在方法上,FairWell测试:
GestureDetector(
onTap: click1,
child: Text('click1'),
),
GestureDetector(
onTap: click_fairwell,
child: Text('click_fairwell'),
),
void click1() {}
@FairWell('click_fairwell')
void click_fairwell() {}
生成json:
{
"className": "GestureDetector",
"na": {
"onTap": "@(click1)",
"child": {
"className": "Text",
"pa": [
"click1"
]
}
}
},
{
"className": "GestureDetector",
"na": {
"onTap": "#(click_fairwell)",
"child": {
"className": "Text",
"pa": [
"click_fairwell"
]
}
}
},
生成js:
click1: function click1() {
const __thiz__ = this;
with(__thiz__) {}
},
click_fairwell: function click_fairwell() {
const __thiz__ = this;
with(__thiz__) {}
},
所以主要区别在于 添加@FairWell 则为
#(click_fairwell) 和不添加FairWell则为: @(click)
用在对象上:
getS4() {}
@FairWell('getS')
getS() {}
转化json如下:
{
"className": "Container",
"na": {
"color": "#(Colors.red)",
"child": "$(getS)"
}
},
{
"className": "Container",
"na": {
"color": "#(Colors.red)",
"child": "%(getS4)"
}
}
即:
对象:添加FairWell为 %(getS4) 不添加为 $(getS)
方法:添加FairWell为#(click_fairwell) 不添加FairWell则为: @(click)
运行widget
1、添加FairWell- #(click_fairwell)
#对应:
class ComponentExpression extends Expression {
@override
R onEvaluate(
ProxyMirror? proxy, BindingData? binding,Domain? domain, String? exp, String? pre) {
var processed = exp?.substring(2, exp.length - 1);
var widget = proxy?.componentOf(processed);
if (widget != null) return R(widget, exp: processed);
if (processed?.contains('.') == true) {
var s = processed!.split('.');
assert(s.length == 2, 'expression is not supported => $exp');
var obj = s[0];
var prop = s[1];
var r = proxy!.componentOf(obj);
if (r is Map) {
assert(!(r[prop] is Function),
'should be an instance of widget or const value');
return R(r[prop], exp: processed);
}
}
return R(null, exp: processed);
}
@override
bool hitTest(String? exp, String? pre) {
return RegExp('#\\(.+\\)', multiLine: true).hasMatch(exp ?? '');
}
}
ComponentExpression主要是 proxy?.componentOf(processed); 和 proxy!.componentOf(obj); 是为了查找组件block映射表 _provider.loadTag(key);查找是否有对应的block生成组件的映射对象。
在namedString和positioned递归解析Json页面widget树过程中,遇到
"onTap": "@(click1)",
"onTap": "#(click_fairwell)",
调用
var r = proxyMirror?.evaluate(context, bound, v, domain: domain);
还原dart界面过程中使用到这个方法进行处理,但是这个方法有个特别之处:
W<dynamic> evaluate(
BuildContext? context, BindingData? bindingData, String text, {Domain? domain}) {
var pre = '';
for (var exp in _expressions) {
if (!exp.hitTest(text, pre)) {
continue;
}
var result = exp.onEvaluate(this, bindingData, domain, text, pre);
pre = result.exp??'';
if (result.valid()) {
return W<dynamic>(result.data, result.needBinding);
}
}
return W<dynamic>(text, false);
}
如果 通过 #(click_fairwell) 即click_fairwell找不到对应的组件(从fair_version或者GeneratedModule的组件映射库中不存在这个名字的组件: 即从 FairWidgetBinding 中查找)
已实现的FairWidgetBinding有:
- common.dart
- flow.dart
- geometry.dart
- fair_version package中实现的FairWidgetBinding
bool valid() {
if (data is String) {
String strData = data as String;
return strData.length > 0;
}
return data != null;
}
result.valid()是为false的。 则继续for循环遍历剩余的_expressions 而我们一共有这么多表达式
final List<Expression> _expressions = [
ComponentExpression(),
InlineExpression(),
InlineObjectExpression(),
WidgetParamExpression(),
FunctionExpression(),
GestureExpression(),
PropValueExpression(),
ValueExpression(),
];
所以最后都会执行到 ValueExpression这个表达式
class ValueExpression extends Expression {
@override
R onEvaluate(
ProxyMirror? proxy, BindingData? binding, Domain? domain, String? exp, String? pre) {
var prop = binding?.bindDataOf(pre ?? '') ??
binding?.modules?.moduleOf(pre ?? '')?.call();
if (prop is ValueNotifier) {
var data = _PropBuilder(pre ?? '', prop, proxy, binding);
binding?.addBindValue(data);
return R(data, exp: exp, needBinding: true);
}
return R(prop, exp: exp, needBinding: false);
}
@override
bool hitTest(String? exp, String? pre) {
return true;
}
}
这个binding?.bindDataOf(pre ?? '')
方法实现如下:
dynamic bindDataOf(String key) {
if (_values != null && _values![key] != null) {
return Function.apply(_values![key]!, null);
} else if (data != null && data![key] != null) {
return data![key];
}
return functionOf(key);
}
所以这里同样也是会先查找 FairDelegate 中有没有定义变量,如果没有就找有没有定义function
2、不添加- @(click)
@对应:
class GestureExpression extends Expression {
@override
R onEvaluate(
ProxyMirror? proxy, BindingData? binding, Domain? domain, String? exp, String? pre) {
var prop = binding?.bindFunctionOf(exp?.substring(2, exp.length - 1) ?? '', proxy, binding, domain, exp: exp);
return R(prop, exp: exp, needBinding: false);
}
@override
bool hitTest(String? exp, String? pre) {
return RegExp(r'\@\(.+\)', multiLine: false).hasMatch(exp ?? '');
}
}
bindFunctionOf 绑定方法:
dynamic bindFunctionOf(String funcName, ProxyMirror? proxyMirror,
BindingData? bound, Domain? domain,
{String? exp}) {
if (_functions?[funcName] == null) {
if (RegExp(r'.+\(.+\)', multiLine: false).hasMatch(funcName)) {
var rFuncName = funcName.substring(0, funcName.indexOf('('));
var params = funcName.substring(
funcName.indexOf('(') + 1, funcName.lastIndexOf(')'));
var args = params.split(',').map((e) {
if (RegExp(r'\^\(index\)', multiLine: false).hasMatch(e) &&
domain is IndexDomain?) {
return domain?.index;
} else if (domain != null && domain.match(e)) {
return domain.bindValue(e);
} else {
var r = proxyMirror?.evaluate(null, bound, e, domain: domain);
if (r?.data == null) {
return e;
} else {
return r?.data is ValueNotifier ? r?.data.value : r?.data;
}
}
}).toList();
//返回的是一个闭包函数。可以携带参数
return ([props]) {
var arguments = [];
if (props != null) {
arguments.add(props);
}
if (args != null) {
arguments.add(args);
}
_functions?['runtimeInvokeMethod']?.call(rFuncName, arguments);
};
} else {
return ([props]) =>
_functions?['runtimeInvokeMethod']?.call(funcName, props);
}
} else {
//返回的是一个function函数,不解析参数
return _functions?[funcName];
}
}
@(click1)
_functions 如果delegate中定义了为 delegate实现的方法。
如果没有实现则为([props]) => _functions?['runtimeInvokeMethod']?.call();
这样的一个block 绑定的是走js中的函数。
3、添加FairWell- %(getS4)
onEvaluate :
var regexp = RegExp(r'\%\(.+\)');
var matches = regexp.allMatches(exp ?? '');
var builder = _FunctionBuilder(
matches: matches, domain: domain, data: exp, proxyMirror: proxy, binding: binding);
binding?.addBindValue(builder);
_FunctionBuilder
_FunctionBuilder(
{this.matches, this.domain,
String? data,
ProxyMirror? proxyMirror,
BindingData? binding})
: super(data, proxyMirror, binding) {
matches?.forEach((e) {
var bindProp;
bindProp = binding
?.runFunctionOf(e.group(0)!.substring(2, e.group(0)!.length - 1), proxyMirror, binding, domain);
if (bindProp is ValueNotifier) {
_watchedProps.add(bindProp);
}
});
attach();
}
runFunctionOf:
dynamic runFunctionOf(String funcName, ProxyMirror? proxyMirror,
BindingData? bound, Domain? domain,
{String? exp}) {
if (_functions?[funcName] == null) {
var result;
if (RegExp(r'.+(.+)', multiLine: false).hasMatch(funcName)) {
var rFuncName = funcName.substring(0, funcName.indexOf('('));
var params = funcName.substring(
funcName.indexOf('(') + 1, funcName.lastIndexOf(')'));
var args = params.split(',').map((e) {
if (RegExp(r'^(index)', multiLine: false).hasMatch(e) &&
domain is IndexDomain?) {
return domain?.index;
} else if (domain != null && domain.match(e)) {
return domain.bindValue(e);
} else {
var r = proxyMirror?.evaluate(null, bound, e, domain: domain);
if (r?.data == null) {
return e;
} else {
return r?.data is ValueNotifier ? r?.data.value : r?.data;
}
}
}).toList();
//执行js,从js获取结果,可以携带参数
result = _functions?['runtimeInvokeMethodSync']?.call(rFuncName, args);
} else {
result = _functions?['runtimeInvokeMethodSync']?.call(funcName);
}
try {
var value = jsonDecode(result);
return value['result']['result'];
} catch (e) {
throw RuntimeError(errorMsg: result);
}
} else {
//调用fair delegate的function,不处理函数参数
return _functions?[funcName]?.call();
}
}
4、 不添加-$(getS)
匹配不到,走 ValueExpression
R onEvaluate(
ProxyMirror? proxy, BindingData? binding, Domain? domain, String? exp, String? pre) {
var prop = binding?.bindDataOf(pre ?? '') ??
binding?.modules?.moduleOf(pre ?? '')?.call();
if (prop is ValueNotifier) {
var data = _PropBuilder(pre ?? '', prop, proxy, binding);
binding?.addBindValue(data);
return R(data, exp: exp, needBinding: true);
}
return R(prop, exp: exp, needBinding: false);
}
bindDataOf:
dynamic bindDataOf(String key) {
delegate 中的value
if (_values != null && _values![key] != null) {
return Function.apply(_values![key]!, null);
} else if (data != null && data![key] != null) {
// data是fairProps数据
return data![key];
}
return functionOf(key);
}
functionOf
//delegate中的function
Function? functionOf(String key) {
return _functions != null ? _functions![key] : null;
}
binding?.modules?.moduleOf:
FairModuleBuilder? moduleOf(String name) => _modules[name];
总结
- 函数名前面添加@FairWell 转化的json区别是
方法:添加FairWell为 #(xx) 不添加为 @(xx)
对象:添加FairWell为 %(xx) 不添加为 $(xx)
- @表达式实现为GestureExpression,返回的是一个函数类型对象,如果fairdelegate中绑定了function走代理,如果没有则为调用fair运行时函数调js函数。
- #表达式实现为先在ComponentExpression中查找name-组件的映射{"组件名字":(props)=>XXWiget()}如果没有找到再通过 ValueExpression 处理,先在 FairDelegate 中定义的的变量_values中找,然后在到页面的FairProps 数据data中找,最后从代理中的Funcitons中找。
- 因此对于这里的点击方法(该方法名字为在map映射组件库中没有定义的name,并且我们是想执行delegate实现的function),在这种实例情况下,在编写动态界面时是否添加FairWell注解效果是相同的,都是走代理。
- @FairWell注解在新版本fair中一般可以不用了
表达式类 | 字面量实例 | 正则匹配 |
---|---|---|
ComponentExpression | #(xxx) | '#\(.+\)' |
InlineObjectExpression | $xxx | r'${\w.+}' |
InlineExpression | ${xxx} | r'$\w+' |
WidgetParamExpression | $(widget.xxx) | '#\(widget..+\)' |
FunctionExpression | %(xxx) 、支持嵌套:%(xxx(^(xxx1),^(xxx2))) 、特殊名字 index:%(xxx(^(index)) | r'%(.+)' |
GestureExpression | @(xxx) | r'@(.+)' |
PropValueExpression | ^(xxx) | r'^(\w+)' |
ValueExpression | 任何其他字符串 | ---- |