作者:闲鱼技能——光酒
闲鱼作为一个二手闲置交易平台,卖家发布产品产出优质的供给尤为重要;产品发布器希望拥有富文本修改才能,让用户简单便捷的方法产出更加优质的内容;Flutter本身没有富文本修改器的才能的,只有最基础的文本修改器TextField;关于更加杂乱的场景,比方支撑自界说表情、主题、有序阶段等才能,现在flutter组件是无法满意咱们的事务诉求,别的在交互体会上与Native仍然存在必定的距离;为了解决事务中面临的以上问题,咱们决议规划并完成一个Flutter场景下高功能、可扩展的富文本修改器。
富文本修改器全体架构规划
首先咱们来看一看全体的架构规区分层:
自下而上首要分四层:
- 1. 协议层:首要担任Model的界说、Selection描绘、Commond事情逻辑处理,以及协议Normalizing校验;
- 2. 才能扩展层:才能扩展层提供丰厚的plugin才能,既有内置的plugin,如:纯文本转化,undo/redo等才能,一起也十分便利的支撑事务层自界说的扩展,例如支撑站外H5页面展示的model to HTML的序列化plugin;
- 3. 烘托层:烘托层首要完成将富文本Model转化成Flutter Widget烘托,以及光标、选区、ToolBar等计算和烘托,以及用户手势交互事情等;
- 4. 事务扩展层:在Mural的规划之初,可扩展便是规划进程中十分重要的一部分,咱们为事务方提供了十分灵活、功能强大的扩展才能,经过自界说Node、Plugin、Normalizing,完成如自界说表情、主题、阶段、语法高亮等才能;
协议层规划
富文本修改器对咱们来说并不生疏,开展至今,已经涌现出十分多有优异的开源富文本修改器;当咱们想要做Flutter富文本协议的时分,第一个主意便是先了解优异的开源富文本修改器方案,防止闭门造车;
现在比较优异的开源富文本修改器,如CKEditor、Quill、Prosemirror、Draft、Slate等等;在了解和比照过后,咱们决议使用Slate作为咱们的富文本修改器的协议;
why Slatejs
咱们为什么挑选Slate?
插件是一等公民,能够很好的满意咱们关于扩展性的要求; Slatjs在规划上支撑嵌套结构,能够满意杂乱的事务场景;
与Dom相同的Data model,关于后面flutter烘托层的完成,也变得更加便利;
直观的指令规划,能够十分好的支撑plugin的自界说扩展;
Slate在规划上,协议层与烘托层是有清晰的中心区分,这让咱们能够复用Slate协议层的规划,烘托层交给flutter来处理;
除了上面的原因,咱们挑选Slate别的一个很重要的原因,便是它的单元测试覆盖率和完好度,让咱们对它的稳定性更有决心;
Slate协议层规划
协议层的全体架构规划如下图:
下面咱们就以Slate为例,来看一看富文本修改器的协议层规划,需求界说的中心概念和模块:
- 1. 嵌套Model界说;
- 2. 原子才能Operation规划;
- 3. 次序维护者Normalizing的规划;
协议层规划——嵌套Model规划
Slate界说了三种类型的Node节点:
- • Editor:包含整个文档内容的根节点;
- • Element:在自界说域中拥有语义的容器节点;
- • Text:包含文档文本的叶子节点;
Editor
Editor抽象接口界说如下:
abstract class BaseEditor {
BaseEditor();
Selection selection;
List<Operation> operations;
Map<dynamic, dynamic> marks;
bool isInline(Element element);
bool isVoid(Element element);
void normalizeNode(Tuple2<Node, Path> entry);
void onChange();
void addMark(String key, dynamic any);
void apply(Operation operation);
void deleteBackward(String unit);
void deleteForward(String unit);
void deleteFragment(String direction);
List<Descendant> getFragment();
void insertBreak();
void insertFragment(List<Node> fragment);
void insertNode(Node node, {Location at});
void insertText(String text);
void removeMark(String text);
}
Element
Element节点比较特殊,既是Ancentor节点,作为容器节点包含子节点;一起又是Descendant节点,能够作为其他容器节点的子节点存在。
- • 块(Blocks):Element默以为Block类型的节点,也便是独立的一个阶段;在Slate协议规划中,一个阶段是不允许存在换行符的,当输入换行符的时分,就会生成一个新的Block类型的Element;
- • 行内(Inlines):一起Element也能够是Inline类型的节点,作为别的一个Element的嵌套子节点存在,作为行内元素烘托在一行;
- • 空元素(Void):Element也能够是Void类型,这儿Void与HTML中Void的是同一个概念:如果某个Node为Void,则表明这个Node节点是不行修改状况,光标无法定位到节点内部,会被全体输入和删去;比方:@某个人、主题、富文本中的图片或许视频等等;
Text
Text节点是树中的最低级叶子节点,描绘了文本内容以及其他自界说的烘托元素;一切的自界说特点都包含在properties
特点中:
class Text implements Descendant {
String text;
Map<String, dynamic> properties;
Text({@required String text, Map<String, dynamic> properties})
: this.text = text ?? '',
this.properties = properties ?? <String, dynamic>{};
@override
String string() {
return text;
}
@override
Text clone() {
return Text(text: text, properties: Map.from(properties));
}
@override
String nativeString() {
return text;
}
}
咱们以下面这这段富文本为例:
终究这样一段富文本对应的Mode界说如下:
能够看到,Model的树形结构还是比较简单的,一切的特点都存放在properties字段中,这也十分便利完成自界说扩展;Flutter烘托层根据Node节点的Type以及properties特点,将富文本内容烘托到屏幕上;
协议层规划——原子才能Operation
接下来需求富文本Commond协议的规划,用户的每一次的文字输入、删去、文字加粗、换行等操作都是一次Command指令;Slate抽象界说了九个最根本的Operations,协议层一切的Commond指令,终究在协议层,都会转化成一个或许多个operation操作:
- • insert_node:刺进Node节点;
- • insert_text:刺进文本;
- • merge_node:兼并相同特点的Node节点;
- • move_node:移动Node;
- • remove_node:删去Node;
- • remove_text:删去文本;
- • set_node:设置Node特点;
- • set_selection:设置Selection;
- • split_node:拆分Node;
下面咱们经过对选中文本加粗
操作为例,来了解Slate协议层Commond的处理进程:
对选中文本加粗
这样一个Commond,协议层会将这个Commond拆解成三个Opeartion:
- •
split_node
:将一个Text Node拆分红三个Text Node; - •
set_selection
:更新光标挑选区域Selection; - •
set_node
:设置需求加粗Text Node节点properties的加粗特点;
当一个Commond被协议层拆分红一个或许多个Opeartion履行之后,会履行一个十分重要的操作——Normalizing;
次序维护者——Normalizing
每一次Command操作,绝大部分情况会对Model进行相应修改;咱们需求一个次序维护者——Normalizing,时间保证对协议Model修改过之后,保持数据结构的正确性;
Slate界说了几个根本的内置Normalizing
规矩:
每一次Commond之后,Editor都会调用normalizeNode
方法,在Normalizing的进程中,发现存在协议结构错误,需求进行错误修复;
Normalizing
的另一个强大之处在于,咱们能够经过自界说Normalizing
,添加自界说的校验规矩,完成自界说的需求;在后面的事务扩展章节会,咱们会具体讲解怎么经过自界说Normalizing快速完成一个自界说主题的才能;
总结
现在Mural已经在闲鱼产品发布、产品概况、音讯等场景落地,支撑了自界说表情、主题等事务才能,用户体会方面也有了十分大的提升。
本次首要介绍了富文本修改器Mural全体的架构规划以及协议层的规划;后续咱们会系列文章的方法介绍Mural在烘托层的规划、自界说扩展规划,以及交互体会、功能方面的优化实践,敬请期待!
参阅链接: [1] Slate:github.com/ianstormtay…