“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

preface

With the Spring Festival holiday approaching, the company’s projects are gradually entering the stage of network closure, in short, thousands and thousands of words are finally merged into a sentence:

There’s gonna be a lot of fooling around, hopefully

Back to the topic, this time to say the content as the title stated, jingdong home page top effect;

On Android native, this seems to be a bit of a problem:

The main problem, as this article explains, is that nested sliding events don’t communicate with each other:

Imitation jingdong, Taobao home page, through two layers of nested RecyclerView to achieve the top effect of TAB

In Android, gestures are passed between views, if not handled by the ViewPager itself, based on ViewParentCompat and NestedScrollChild and NestedScrollParent. Basically, layer by layer View distribution; The problem with this is that if you insert a layer of View between two layers of nested sliding views, and the View does not support nested sliding decay, no one will then distribute the event, and you will not be able to implement nested sliding.

Of course, this is just one of the solutions, you can do it with CoordinatorLayout as well; But you still have to customize it to solve the problem of knowing which of the Child’s views to slide into; Such as this:

PersistentCoordinatorLayout

You can’t do it in one step;

The Flutter does not have this problem:

The ability of a Flutter to find relationships across 18 generations is very simple. NestedScrollView also implements event distribution based on this ability: in other words, NestedScrollView can do this without having to process it itself.

So with that in mind let’s see how the NestedScrollView of Flutter is implemented:

1, How can NestedScrollView be able to connect the parent class and the child class through several layers?

NestedScrollView is a Scrollable view. All widgets that can be slid are Scrollable.

So the analysis of Scrollable, the natural place to focus on is its Position and ScrollController, and the gap between NestedScrollView and ListView is not very big, the process is still the same;

But in the controller part of the Scrollable, NestedScrollView implements the ScrollController itself and assigns its own Scrollable and passes the child’s PrimaryScrollController, Another constructed ScrollController is assigned to a child (so if the child ListView is set to a Controller, NestedScrollView does not support nested sliding, for this reason);

And then the ListView can get the scrollController from PrimaryScrollController and set it to Scrollable;

So, NestedScrollView can operate both its own Scrollable and the corresponding Scrollable of its children. Mastering ScrollController is not much different from being Scrooable;

Meanwhile, both scrollControllers are _nestedScrollControllers and are created by the _NestedScrollCoordinator class. At creation time, the Coordinator also passes itself in, so that the parent Scrollable, child Scrollable, and Coordinator can get information about each other.

2. How does NestedScrollView handle gesture operations?

In terms of general flow, NestedScrollView is not much different from the gesture operation in Android mentioned above.

Step by step:

Mask gesture handling for child Scrollable

In Android, if there is nesting in RecyclerView, its external RecyclerView will completely take over gesture events; In Flutter, this is a similar idea, but implemented in a slightly different way;

As you saw in the previous ListView analysis series, the gesture notifier is officially activated by calling setCanDrag(true); And the setCanDrag method that calls this is ScrollPosition again;

The same is true for NestedScrollView, but since it’s using its new _NestedScrollController, its ScrollPosition is also _NestedScrollPosition;

If you go to the setCanDrag method where applyNewDimensions is called, you can see that the applyNewDimensions of the position corresponding to the child Scrollable will not be called. SetCanDrag is only called for the outPosition of the outer layer;

The child Scrollable will never call setCanDrag, so it can’t activate the notifier or compete with the gesture, and the gesture will completely have an outer Scrollable to take over.

The updateCandarg method in coordinator:

UpdateCanDrag in NestedScrollPosition:

You can see that there is no concept of gesture interception in Flutter, or that gesture interception can be done by not distributing gestures directly, or by excluding subviews from gesture processing and not adding them to the gesture competitor, or by allowing subviews to lose the gesture competition; Although the idea and the end result are the same, the process is different from Android’s gesture event flow processing model;

Gesture distribution processing

Above, the child Scrollable’s gesture operation permission is cancelled, and all gesture events are handed over to the parent Scrollable for processing. And what the parent Scrollable does is, when you update your pixel, it handles the over-sliding part and passes it on to the child Scrollable.

To do this, all activities are also synchronized to the child Scrollable; In other words, when the parent Scrollable starts the DragActivity, so does the child Scrollable; Fully synchronize parent Scrollable at the activity level;

See the specific process:

First, NestedScrollPosition assigns these operations to the coordinator for a unified distribution:

In this case, the delegate is set to the coordinator itself, representing the future computational processing object of the ScrollActivity as the coordinator class:

Take the Drag event as an example. In DragActivity, the response to a gesture update is update, and the gesture information is handed to a delegate to handle the update. In NestedScrollView, the coordinator mentioned above;

It does a uniform distribution of updated gestures:

Processing logic in the UP direction:

Processing logic in the Down direction

To put it simply:

Processing logic in the UP direction:

If the child Scrollable is over-sliding and the Pixel is negative, then the child Scrollable handles the pixel first, and then the parent Scrollable handles the pixel when it is reset to 0. If the parent Scrollable can’t consume over-sliding gestures, then the child Scrollable handles the pixel.

Processing logic for Down direction:

If there is a float mode, the outer layer handles it first; It is then consumed by the child Scrollable; The part that can’t be consumed is handed over to the outer layer; If you can’t consume it, you pass it to Scrollable;

conclusion

This article provides a simple analysis of how NestedScrollView implements coordinated nested sliding across widgets. In accordance with international convention, this should have an effect:

I have to say, the idea of the widget model is sometimes really useful