随着 Flutter 3.3 正式版发布,Global Selection 终于有了官方的正式支持,「该功能补全了 Flutter 长时间存在 Selection 异常等问题,特别是在 Flutter Web 下经常会有选择文本时与预期的行为不匹配的情况」。
使用
「使用 SelectionArea 也十分简单,如下代码所示,只需要在您想要支持的地方添加 SelectionArea 即可」,甚至可以在每个路由下的Scaffold 添加SelectionArea 来全面启用支持。
默认情况下SelectionArea 已经实现了所有常见的功能,并且 Flutter 针对不同平台进行了差异化实现,如下图所示 Android 和 iOS 会有不同的样式效果。
所以如果您觉得这个判断有问题,完全可以自己 override 一个自定义的 TextSelectionControls,比如在canSelectAll 直接 return true。
是的,「对于SelectionArea 我们可以通过继承 TextSelectionControls 来自定义」:
通过 buildToolbar 自定义弹出的 Toolbar 样式和逻辑,甚至您可以添加一些额外的标签能力,比如 "插入图片"; 通过 buildHandle 自定义 Selection Handle 可拖动部分的样式。
而在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 内部的 SelectionContainer 是有对应的 registrar 实现往下共享; SelectionContainer.disabled 内部的registrar 是 null。
△图二
到这里您应该大致理解了如何使用和自定义一些 SelectionArea 的能力,那么接下来介绍两个 "Bug",通过这两个 "Bug" 我们深入理解SelectionArea 内部的实现情况。
问题 1
如下代码所示,「当使用了WidgetSpan 之后,默认情况下,用户在开始位置拖拽 Handle 进行选择时会无法选中WidgetSpan 里的文本」。
PS:其实拖动可以选中,只是这里暂时以不能选中的情况下作为切入点。
当然,「其实在拖动Handle还是可以选中WidgetSpan里的文本,比如您从HelloWorld开始拖动,这里拖动选中不了的原因后面会解释」。
问题2
如果当我们点击了全选会怎么样?如下图所示,在我们点击全选之后,可以看到两个 "奇怪" 的问题:
WidgetSpan 里的 Hello World 可以被选中了; 左侧的 Start Handle 位置不是在文本开头,而是在WidgetSpan 开始。
我们首先看第一点,「为什么点击全选时,WidgetSpan 里的 Hello World 可以被选中」?
其实全选操作和拖拽 Handle 最大的不同就是: 它是往下直接发出全选事件 SelectAllSelectionEvent,而该事件会触发所有 child 响应事件,自然也就包括了 WidgetSpan 里的 Hello World。
_handleSelectAll 获取得到 _textSelectionStart 和 _textSelectionEnd,表明此时控件已经被选中; didChangeSelection 里通过 paragraph.markNeedsPaint() 触发重绘,然后增加选中时的覆盖颜色。
首先我们看,为什么复制出来之后的内容会是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"
原文标题:Flutter 3.3 之 SelectionArea 好不好用?用 "Bug" 带您全面了解它 | 开发者说·DTalk
文章出处:【微信公众号:谷歌开发者】欢迎添加关注!文章转载请注明出处。
-
谷歌
+关注
关注
27文章
6128浏览量
105021
原文标题:Flutter 3.3 之 SelectionArea 好不好用?用 "Bug" 带您全面了解它 | 开发者说·DTalk
文章出处:【微信号:Google_Developers,微信公众号:谷歌开发者】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论