This is the sixth day of my participation in the First Challenge 2022. For details: First Challenge 2022.

preface

In the previous article, I did a simple analysis of how ReOrderables implement drag and drop sorting. After the simple analysis, in addition to learning some interesting operations, one thought came to mind:

This code, why so many calculations and adjustment parts…

As a good git of the new socialist era, code like this, riddled with calculations and modifications, must be too lazy to look at;

So here’s the question:

Is there a way to do this drag-and-drop sort without calculating, with less code?

Effect of the design

In reOrderables, most of the code is used to locate the sorting position. Finally, a folding animation is implemented by adjusting the Size and using the AnimationController to assist the processing.

To be honest, the effect is there, but the code does make people dizzy;

On Android, more sort animations are similar to panning:

So THIS time I plan to use this animation implementation as well (actually lazy);

Of course, there should be shadows and the like;

Implementation scheme:

Get the location information of each Item

To achieve the above translation animation, the first step is to know the position of each Item itself in the GridView.

In most scenarios, we measure the location by adding a GlobalKey to each Item and then retrieving the renderObject.

But for a little fun, I wanted to reduce the number of GlobalKeys while minimizing the amount of code;

As we all know, sliver calls the didFinishLayout method after the layout method is finished to notify layout that it is finished and to publish the index of the first and last Item in the current cache.

In this case, when I end the layout method, I can get the starting position of each Item.

Provide an InheritedWidget that shares the Item location

[InheritedWidget] [InheritedWidget] [InheritedWidget] [InheritedWidget] [InheritedWidget] [InheritedWidget]

As for what these two lists are for, I will reveal them later.

PS: Actually, it is not appropriate to use List to store location information, because the first Item in the List is not necessarily the same Item in the GridView whose index = 0.

I’m going to be lazy here, so I’m going to use List

Give me a Key and give you an Offset

Inherit the GridView, rewrite buildChildLayout, and give SliverGrid a GlobalKey:

This Key retrieves the Element of the SliverGrid; By using Element, you can get the corresponding Element of each child.

The slight difference here is that since the name of the method is didFinishLayout, this is actually the last step of the performLayout method, so the performLayout method is not actually finished;

So the parent default are not allowed to access the size of the child, if the call renderOject. Translate this point using localToGlobal complains;

In this case, we can get the location of the child in another way, for example, ParentData:

PS: The Element that calls the SliverChildDelegate didFinishLayout method is the Element in the SliverGrid. To be clear, if you are diligent, Rewrite the sliver to pass Element;

Also, because Element can be seen as a BuildContext, the context can also be retrieved, which in turn can be used to retrieve inheritedWidgets and share data easily.

So I think the best way to do this is to rewrite sliver and the SliverChildDelegate…

The Gospel of lazy people AnimatedContainer

Here we do some processing on the build Item, first enclosing an AnimatedContainer:

AnimatedContainer is a fully automatic animation player. There is no AnimationController or Tween. Only a setState can easily switch between animations.

The contents of the previous two lists are used here:

Just need to subtract the Offset of two items, setState can realize the position translation animation;

PS: This project comes from a German brother’s project

However, I feel that he also wrote quite troublesome, not combined with DragTarget, but only through the Draggable return gesture information to do collision detection, see a little dazzling…… 😵 💫

As for how the contents of the two lists have changed, here is the section:

Pull My “Draggable” Trigger !

Here comes the familiar Draggable+DragTarget part:

In the figure above, the last Item constructed is a Stack, where the child of the original Item is passed to the buildDraggable method to construct the Draggable;

The other part is the DragTarget, and the key part is here. What it does is reorder the index of the Item and itself after detecting the collision:

Finally, the external setState is triggered by the callback to start the animation:

Conclusion:

The two files total less than 300 lines:

Although the target effect is achieved in less than 300 lines, there are still some bugs. For example, when the DragTarget is switched again, the location information is wrong, and it is estimated that a certain state is not saved, and the notification to save data after onDragEnd is not processed

But 300 lines does do the core logic, and the rest is just a bunch of bits and pieces… Maybe ~~~~~

Back to the main topic: international practice, let’s see the effect: