本人在某電商公司搬磚,一般來說電商APP的首頁都需要非常動(dòng)態(tài)靈活的展現(xiàn)方式,例如:某天產(chǎn)品突發(fā)奇想為后天的xx大促活動(dòng)設(shè)計(jì)了一個(gè)酷炫的UI展現(xiàn)方式,跟技術(shù)說如果按照我的這種展示交互來做的話,這個(gè)轉(zhuǎn)化率肯定能提升100%。問我們什么時(shí)候能搞定,我們一評(píng)估發(fā)現(xiàn)這個(gè)需要發(fā)版才能搞定。后天的xx大促肯定是來不及了。產(chǎn)品會(huì)覺得我們技術(shù)太差,我們也在產(chǎn)品面前抬不起頭。

對(duì)于這種問題,APP有React Native、Weex這種解決方案,但是我們調(diào)研RN后發(fā)現(xiàn),RN有很多問題,而且性能方面也不是太好。好像現(xiàn)在越來越多的公司也已經(jīng)拋棄RN了。所以我們就決定自己定義了一套UI組件協(xié)議,在Android、iOS,JS設(shè)置小程序端分別實(shí)現(xiàn)。這種方案前期工作量確實(shí)很大,但是當(dāng)你的UI組件協(xié)議越來越豐富時(shí),帶來的好處也是極大的。APP中的很多頁面我們都能做到隨時(shí)更新。甚至是能做到隨時(shí)新增一些頁面。避免APP一旦修改都需要發(fā)版的問題。極大的提升了產(chǎn)品迭代速度。帶來極大的收益。
Flutter
其實(shí)早在17年的時(shí)候,我們就已經(jīng)有關(guān)注Flutter了,那時(shí)候還是叫Sky。我們?cè)趫F(tuán)隊(duì)內(nèi)分享后覺得還是處于非常早期的階段,后面也沒有再持續(xù)關(guān)注了,沒想到Flutter在18年12月份放出了1.0正式版。Flutter天然的跨平臺(tái),支持Android,iOS。Web和桌面也在支持的計(jì)劃中??梢哉f幾乎跨了所有的平臺(tái)。而且是基于同一份代碼,注意這里跟RN還是有區(qū)別的,RN是Learn once, write everywhere.
在學(xué)習(xí)Flutter的過程中發(fā)現(xiàn)寫Flutter的Widget跟我們公司定義的那套UI協(xié)議是非常相似的。我們定義的UI協(xié)議是基于json語法,而Flutter的Widget代碼跟json幾乎一致。

這里可以把Widget的類型對(duì)應(yīng)到j(luò)son string的type屬性。其他的屬性基本保持一致,這里color的屬性在json中使用#aarrggbb就行。看到這里心中就涌現(xiàn)一個(gè)想法,如果我用Flutter來實(shí)現(xiàn)一個(gè)DynamicWidget,這個(gè)Widget可以通過json來創(chuàng)建的話,這樣我只要用Flutter實(shí)現(xiàn)一遍就能天然的跨多端。而且Flutter的Widget就是天然的UI協(xié)議呀,我只要把Flutter官方的Widget轉(zhuǎn)成json然后實(shí)現(xiàn)一遍就行了。這樣用戶只要會(huì)了解了Flutter的Widget基本上就能寫出對(duì)應(yīng)的json了。用戶不需要再理解一套特殊的UI協(xié)議。我們公司的UI協(xié)議,為了兼容Android、iOS各端走的是一套特殊的UI協(xié)議,需要使用者去學(xué)習(xí)。
Flutter Dynamic Widget
說干就干,先定義一個(gè)WidgetParser接口。
/// extends this class to make a Flutter widget parser.
abstract class WidgetParser{
/// parse the json map into a flutter widget.
Widget parse(Map<String, dynamic> map);
/// check the matched widget type. for example:
/// {"type" : "Text", "data" : "Denny"}
/// if you want to make a flutter Text widget, you should implement this
/// method as "Text" == widgetName, for more details, please see
/// @TextWidgetParser
bool forWidget(String widgetName);
}
parse方法解析json得到Flutter的Widget,forWidget方法表示這個(gè)Parser是對(duì)應(yīng)json中對(duì)應(yīng)的type屬性。我們來實(shí)現(xiàn)一個(gè)Container widget的Parser, 如下:
class ContainerWidgetParser extends WidgetParser{
@override
bool forWidget(String widgetName) {
return "Container" == widgetName;
}
@override
Widget parse(Map<String, dynamic> map) {
Alignment alignment = parseAlignment(map['alignment']);
Color color = parseHexColor(map['color']);
BoxConstraints constraints = parseBoxConstraints(map['constraints']);
//TODO: decoration, foregroundDecoration and transform properties to be implemented.
EdgeInsetsGeometry margin = parseEdgeInsetsGeometry(map['margin']);
EdgeInsetsGeometry padding = parseEdgeInsetsGeometry(map['padding']);
Map<String, dynamic> childMap = map['child'];
Widget child;
for(var parser in DynamicWidgetBuilder.parsers){
if (parser.forWidget(childMap['type'])){
child = parser.parse(childMap);
break;
}
}
return Container(
alignment: alignment,
padding: padding,
color: color,
margin: margin,
width: map['width'],
height: map['height'],
constraints: constraints,
child: child,
);
}
}
你需要做的就是解析json的各個(gè)屬性,然后最后拼裝一個(gè)Container對(duì)象。很簡(jiǎn)單把?;谝陨系南敕ǎ野褜?shí)現(xiàn)放在github上,大家可以在這里查看源碼和各個(gè)實(shí)現(xiàn)的Widget的demo。同時(shí)也發(fā)布到dart pub上了。
github:https://github.com/dengyin2000/dynamic_widget
dart pub:https://pub.dartlang.org/packages/dynamic_widget
目前只是實(shí)現(xiàn)了幾個(gè)Flutter Widget,計(jì)劃是把Flutter官方的widget都實(shí)現(xiàn)一遍。大家如果有問題的話,歡迎隨時(shí)聯(lián)系我。
