「这是我参加2022初次更文应战的第8天,活动详情检查:2022初次更文应战」。

前语

这篇是对书架功用的一个小总结,趁便介绍下基本功用以及完结方法技能点,另外对最近的张狂摸鱼做一番胡适式检讨;

开门见山的说,先放一下作用图:

【Flutter】基于 Draggable + DragTarget + GridView 还可以这么玩 —— 书架功能的实现

总结一下的话,功用点就这么两个:

  • 依据GridView的重排序功用;
  • 依据Draggable + DragTarget 的手势处理

虽说功用点就这么两个,但是涉及到的知识点也的确不少,挨个总结一下,看看是不是跟你想象的计划相同:

完结与技能点总结

依据GridView的重排序功用:

原理解析:

假如仅仅指这个标题所述的作用,相信不少人闭着眼用舌头都能敲出好几种计划,比如说最基本的 GridView.builder + setState 改动 数据源的次序的计划;

假如在这个基础上,加一个长按Item排序呢?

以普遍理性而言,这时分那批用舌头敲代码的人,应该应该要睁开眼睛了;

关于他们来说,现在提出的计划,可能是 GestureDector + GestureBinding.hitTest ,然后遍历 HitTestResult.path ?再次点,给一切Item 加上GlobalKey ,不断Item,获取手势方位是否在Item范围内,从而得知重排序的次序并 setState ;

假如在加个 Item长按拖拽移动 / 虚影 呢?

那就给长按的Item 加个OverLay ?

假如再加个重排序动画呢 ?

全体 Item 都加 Tansform ? 然后再各个核算 Item transform的 结果;

可能到这儿,有人就感觉,上面这套计划最后完结出来的作用,会是一坨稀饭;但实际上,这套计划的确是可行的,也是我的完结的最最最基本的原理,只不过其间绝大部份东西,Flutter都现已供给好了,只需求正确组合运用即可;

技能选型:

依据上面的基本思路,我挑选的技能计划是 自定义GridView + Draggable + DragTarget + AnimateCotnainer的计划:

1、自定义GridView

为什么需求一个自定义的GridView呢?其作用是什么

首要作用是供给重排序的功用;

当然,这时分可能有彦祖发弹幕提问:

之前不是说了,直接用 setState 调整数据源的次序,不就可以完结重排序了么?

当然,setState 的确可以完结重排序,但是由此会导致 itemBuilder 重建Item ,从而导致item自身重建,终究导致 item中所持有的状况全部初始化

这会导致什么问题呢?这时分假如你运用Draggable,就会发现其间的状况因为初始化,很多东西就显现反常,比如说childWhenDragging 显现的Item 被回复为原 child,原先记载的手势信息也会被初始化,导致回调触发反常;

所以说,还不能触发Item自身的重建,或者说,只能答应重建Item的内容,其自身不答应被修正(感觉说的有些绕,总归,做法便是打脏Item自身,而非GridView,打脏GridView就会导致Item重建);

这时分我挑选的计划是调整 GridView 中 child 的Element 和 RenderObject的次序,而非数据源的次序,并在必要的时分,打脏Item自身使其重建,这样既能坚持原有Item中的状况,又调整了其在GridView中的次序;

2、Draggable+DragTarget

这两个在重排序这个主题下的作用,便是当作获取被操作Item和目标Item方位的作用;

不过需求注意的一点是,因为上面的计划调整的是各个Item在GridView的RenderObject层面的方位,而非GirdView的数据源的方位,那么就会出现一个问题:

怎么获取Draggable和DragTarget在触发重排序的时分,其各安闲RenderObject层面中的方位?

在这儿,我的做法,是在各个Item外面再包一层InheritedWidget,其间存储着包括RenderObject方位信息在内的各种信息,在触发重排序的时分,获取这个InheritedWidget读取对应信息即可:

【Flutter】基于 Draggable + DragTarget + GridView 还可以这么玩 —— 书架功能的实现

【Flutter】基于 Draggable + DragTarget + GridView 还可以这么玩 —— 书架功能的实现

这个ItemData中方位信息的保护作业,就由上面的那个自定义GridView顺带完结:

【Flutter】基于 Draggable + DragTarget + GridView 还可以这么玩 —— 书架功能的实现

3、AnimatedContainer

AnimatedContainer 的作用便是供给一个重排序的交流动画

不过这块跟原版的AnimatedContainer比较,还是需求必定的自定义处理;

因为为了保存包括Dragable在内的Item状况,因而AnimatedContainer 的状况也因而得到了保存;由此带来的的一个问题便是:

这回导致动画会从上一次完毕的偏移量开始;而重排序后的Item自身方位就发生了改动,再加上上一次动画偏移量的影响,终究作用便是位移动画反常,彻底不是从原方位到目标方位的作用;

所以为了解决这个问题,需求对AnimatedContainer做必定的修正,我的做法是让其只播映从给定偏移量到偏移量0的一个动画:

这儿仅仅重写这个方法,重设动画起点和终点方位即可:

【Flutter】基于 Draggable + DragTarget + GridView 还可以这么玩 —— 书架功能的实现

依据Draggable + DragTarget 的手势处理

这儿所说的手势处理,便是指什么时分是交流重排序手势,什么时分是兼并文件的手势。以及逗留和推迟判别手势这块的处理;

原理剖析:

首要重排序和兼并手势的区别,其实很简单,不少玩过阅览类APP的人脱口就能说出:

无非便是移动到小说封面边缘便是交流重排序,移动到封面中心逗留一段时间便是兼并操作嘛

按这个说法,思路也就出来了:

  • 给DragTarget加个延时操作,用于逗留操作的判别,重排序那块设置少点,比如说200ms,兼并文件的那块就设置个一秒这种;

  • 最底下的DragTarget 是担任重排序判别的DragTarget, 中心再放一个小区域,用来当兼并手势操作的DragTarget;

计划:

关于怎么加个延时操作,做法也很简单,参考 DelayedMultiDragGestureRecognizer 的完结方法,可以用timer加个延时回调,假如触发了其他手势,那么去掉或者重置timer即可;

至于区域这块的处理,就更简单了,直接用一个Stack处理一下就行;

【Flutter】基于 Draggable + DragTarget + GridView 还可以这么玩 —— 书架功能的实现

结语

现在书架这块的功用点完结也基本完结了,剩余的作业除了润色一下,完结一下边缘功用,就到了帖子和社区这块的完结了;