本文原作者: 恋猫de小郭,原文发布于: GSYTech
随着 Flutter 3.3 正式版发布,Global Selection 终于有了官方的正式支持,「该功能补全了 Flutter 长时间存在 Selection 异常等问题,特别是在 Flutter Web 下经常会有选择文本时与预期的行为不匹配的情况」。
使用
「使用 SelectionArea 也十分简单,如下代码所示,只需要在您想要支持的地方添加 SelectionArea 即可」,甚至可以在每个路由下的 Scaffold 添加 SelectionArea 来全面启用支持。
默认情况下 SelectionArea 已经实现了所有常见的功能,并且 Flutter 针对不同平台进行了差异化实现,如下图所示 Android 和 iOS 会有不同的样式效果。
所以如果您觉得这个判断有问题,完全可以自己 override 一个自定义的 TextSelectionControls,比如在 canSelectAll 直接 return true。
是的,「对于 SelectionArea 我们可以通过继承 TextSelectionControls 来自定义」:
而在 SelectionArea 里,不管是 Handle 还是 Toolbar,都是通过新增 Overlay 来实现样式,这部分的逻辑主要在 SelectionOverlay 对象:
如果您还不了解 Overlay,可以简单理解为: 「默认情况下所有的路由页面都在一个 Overlay 下,打开一个 Route 就是添加一个 OverlayEntry 到 Overlay 里」。
所以 Handle 和 Toolbar 都是通过 OverlayEntry 打开的特殊 "路由" 控件,拥有新的层级,例如下方右图就是 Toolbar 所在的 OverlayEntry。
「另外,对于 Handle 的颜色定义,默认情况下主要来自 TextSelectionTheme 和 Theme」。
例如 MaterialTextSelectionControls 里,start 和 end 两个 Handle 的颜色,默认是通过 TextSelectionTheme 的 selectionHandleColor 或者 Theme 的 primary 来设置。
而对于文字的选中区块的颜色,默认是通过 DefaultSelectionStyle 的 selectionColor 来显示,当然,如下第二张图所示,在 MaterialApp 里它依然和 TextSelectionTheme 的 selectionColor 或者 Theme 的 primary 有关系。
「那如果您还想要在 SelectionArea 下的某些内容不允许被选中呢」?
这里 Flutter 提供了 SelectionContainer.disabled 实现,只要在对应内容嵌套 SelectionContainer.disabled,那么这部分内容下的文本就无法被选中。
为什么嵌套 SelectionContainer.disabled 就可以禁用文本选中的能力?这其实和 SelectionArea 的实现有关系:
SelectionContainer 内部实现了一个 InheritedWidget,它会往下共享一个 SelectionRegistrar,而默认情况下 SelectionArea 内部使用了 SelectionContainer 并且往下共享了对应的 Registrar 实现。
△ 图二
到这里您应该大致理解了如何使用和自定义一些 SelectionArea 的能力,那么接下来介绍两个 "Bug",通过这两个 "Bug" 我们深入理解 SelectionArea 内部的实现情况。
问题 1
如下代码所示,「当使用了 WidgetSpan 之后,默认情况下,用户在开始位置拖拽 Handle 进行选择时会无法选中 WidgetSpan 里的文本」。
PS: 其实拖动可以选中,只是这里暂时以不能选中的情况下作为切入点。
当然, 「其实在拖动 Handle 还是可以选中 WidgetSpan 里的文本,比如您从 Hello World 开始拖动,这里拖动选中不了的原因后面会解释」。
问题 2
如果当我们点击了全选会怎么样?如下图所示,在我们点击全选之后,可以看到两个 "奇怪" 的问题:
我们首先看第一点,「为什么点击全选时,WidgetSpan 里的 Hello World 可以被选中」?
其实全选操作和拖拽 Handle 最大的不同就是: 它是往下直接发出全选事件 SelectAllSelectionEvent,而该事件会触发所有 child 响应事件,自然也就包括了 WidgetSpan 里的 Hello World。
首先我们看,为什么复制出来之后的内容会是 Hello World!Flutter isthe best!?
正如前面说到的,复制调用的是 getSelectedContent 方法,如下代码所示,「可以看到在 selectables 这个 List 的第一位就是 Hello World,所以最终拼接出来的文本会是 Hello World!Flutter isthe best!」 。
目前这个问题在 master 和 stable 分支均可以复现,对应 issue 我也提交在 #111021。
最后
虽然 SelectionArea 的出现补全了 Flutter 的长久以来的短板之一,不过基于 SelectionArea 实现的复杂程度,目前 SelectionArea 还有不少的细节需要优化,但是万事开头难,本次 3.3 SelectionArea 的落地也算是一个不错的开始。
最后,相信通过本文大家应该对 SelectionArea 的使用和实现都有了一定的了解。
长按右侧二维码
查看更多开发者精彩分享
"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。
点击屏末 | 阅读原文 | 即刻报名参与 "开发者说·DTalk"