Flutter从0到1实现高性能、多功能的富文本编辑器(基础实战篇)

前言

上一章中,我们分析了一个富文本编辑器需要有哪些模块组成。在本文中,让我们从零开始,去实现自定义的富文本编辑器。

注:本文篇幅较长,从失败的方案开始分析再到成功实现自定义富文本编辑器,真正的从0到1。建议收藏!

— 完整代码太多, 文章只分析核心代码,需要源码请到 代码仓库,也可在本站下载代码拷贝

错误示范

遭一蹶者得一便,经一事者长一智。——宋·无名氏《五代汉史平话·汉史》

在刚开始实现富文本时,为了更快速的实现富文本的功能,我利用了TextField这个组件,但写着写着发现TextField有着很大的局限性。不过错误示范也给我带来了一些启发,那么现在就让我和大家一起去探索富文本编辑器的世界吧。

最后效果图:

定义文本格式

作为基础的富文本编辑器实现,我们需要专注于简单且重要的部分,所以目前只需定义标题、文本对齐、文本粗体、文本斜体、下划线、文本删除线、文本缩进符等富文本基础功能。

定义文本颜色

定义功能枚举类

定义富文本样式

定义不同样式文本间距

当为list type时,加上前置占位符

封装RichTextField

为了让TextField更好的使用自定义的样式,需要对它进行一些简单的封装。

自定义Toolbar工具栏

这里使用PreferredSize组件,在自定义AppBar的同时,不对其子控件施加任何约束,不影响子控件的布局。

效果图:

全局控制管理

分析需要实现的功能后,我们需要将每一块样式分为一个输入块 (block) 。因此,我们需要存储三个列表,用来管理:

  • List<FocusNode> _nodes = [] 存放每个输入块的焦点
  • List<TextEditingController> _controllers = [] 存放每个输入块的控制器
  • List<List<RichTextInputType>> _types = [] 存放每个输入块的样式

再进一步分析后,我们还需要这些模块:

  • 返回当前焦点所在输入块的索引
  • 插入新的输入块
  • 修改输入块的样式

布局

常用Stack,将工具栏Appbar固定在页面底部。前面我们定义了ChangeNotifier,现在需要使用ChangeNotifierProvider

分析总结

通过上面的步骤,我们就能实现效果图中的功能了。但是,这样实现后,会出现几个对于富文本来说致命的问题:

  • 由于TextField对富文本支持不完善,在对文本添加颜色、文本段落中添加图片时,有较大的困难。
  • 无法选中ListView中未渲染的TextField
  • ...

在遇到这些问题后,我想到了RichText。它除了可以支持TextSpan,还可以支持WidgetSpan,这样在对文本添加颜色,或者在文本中插入图片这样放入Widget的功能时就比较灵活了。对于文本选择问题,通过渲染多个TextField不是个好方案。

正确案例

为了解决分析出的问题,第一点就是,我们不能再渲染多个TextField,虽然也能通过同时控制多个controller来解决部分问题,但是实现成本较高,实现后也会有很多缺陷。所以实现方案要从渲染多个输入块转为一个输入块,渲染多个TextSpan。方案有了,那么让我们开始实现吧!

实现buildTextSpan方法来将文本转化为TextSpan

在之前的基础文本知识篇中,我们知道RichTexttext属性接收一个InlineSpan类型的对象(TextSpanWidgetSpanInlineSpan的子类),而InlineSpan又有一个叫做children的List属性,接收InlineSpan类型的数组。

构建TextSpan

文本输入块的基础实现

为了更好的实现文本输入块,TextField是不能够满足我们的。现在让我们开始实现自己的文本输入块。分析TextEditingController我们可以知道,TextField的最后执行相关逻辑的Widget_Editable,那么我们就要先从它入手。

因为InlineSpan有一个叫做children的List属性,用于接收InlineSpan类型的数组。我们需要通过遍历InlineSpan,在WidgetSpan中创建子部件。

定义了_Editable后,我们需要构建基本的文本输入块。

Flutter 3.0以后,加入了DeltaTextInputClient,用于细分新旧状态之间的变化量。

让我们从用户行为来分析实现BasicTextInput,当用户编辑文字时,需要先点击屏幕,需要我们先获取到焦点后,用户才能进一步输入文字。

当用户编辑文本后,我们需要更新编辑文本的值。

有了基础了编辑文字,那么如何复制粘贴文字呢?

隐藏文本工具栏

不过,当文本发生变化时,需要对文本编辑进行更新时,更新的值必须在文本选择的范围内。

构建_EditableShortcuts是通过按键或按键组合激活的键绑定。

具体参考:https://docs.flutter.dev/development/ui/advanced/actions_and_shortcuts

分析到这里,我们就把自定义的富文本文本输入块实现了。当然,目前还要许多需要扩展和优化的地方,大家有兴趣可以持续关注代码仓库~

尾述

在这篇文章中,我们从0到1实现了基本的富文本编辑器,通过失败的简单案例,在分析吸取经验后实现扩展好的富文本编辑器。在下一篇文章中,会实现更多对富文本编辑器的扩展。希望这篇文章能对你有所帮助,有问题欢迎在评论区留言讨论~

参考

Flutter 快速解析 TextField 的内部原理 — @恋猫de小郭

用flutter实现富文本编辑器

flutter_quill

参考链接


发布者

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注