During the 14 days of isolation, I slowly studied the pointer events of Flutter. In this process, I reviewed the formation process of Element and Render Tree. This article, mainly on the pointer events in Fluter how to send to each component of the process of combing. (A pointer is a pointer, a gesture is a gesture, a gesture is some behavior of a pointer event, and there can only be one winner. Make a clear distinction.)

Looks like a dash. 🤤 🤤 🤤

All right, let’s get down to business. What does Flutter do when you click on the screen?

Process analysis

Through the call stack, we step through the process.

1. Obtain pointer event data from the platform and distribute it

Pointer data is wrapped as DartByteDataType, callPlatformDispatcherthe_dispatchPointerDataPacketMethods, as for how pointer data is wrapped by the platform, what is the content, this relates to the way each platform handles Pointers, nuggets by many related articles. (The author does not currently know any native knowledge)

2. Process the resulting pointer data in GestureBinding and

After several layers of indecipherable calls, the pointer data goes to this place and starts withThe queueThe form is processed. (There is both window and lock, temporarily ignore).

Notice that the packet here isui.PointerDataPacketType, and there is only one inside the classfinal List<PointerData>Data member of.

Since it isThe queue, so you loop out the column and process the events one by one. This process is led by_flushPointerEventQueueTo complete.

handlePointerEventThe method, as its name suggests, the data that is passed here has becomePointerEventType. (resanlingEnabledThe default is false. The official documentation explains this option for devicesPointer sampling rateandScreen refresh rateHas some kind of relationship, can make the pointer more silky)

3. HitTest GestureBinding and RendererBinding

_handlePointerEventImmediately methods are handlePointerEvent (above) to the guy calls. This method is a heavyweight guy.

void _handlePointerEventImmediately(PointerEvent event) {
  HitTestResult? hitTestResult;
  if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) {
    / / to omit...
    hitTestResult = HitTestResult();
    hitTest(hitTestResult, event.position);   // See here!!!!!!!!!!!
    / / to omit...} dispatchEvent(event, hitTestResult); }}Copy the code

So I’m going to pause for a second, and maybe I didn’t write that very well, but the only thing you need to remember is that we’ve got the data event that is a PointerEvent of type PointerEvent.

(PointerEvent has several subclasses, PointerDownEvent, PointerMoveEvent, PointerUpEvent, etc., corresponding to click, move, lift)

Start core content

Here’s an example of a PointerDownEvent, an event that happens when the user clicks on the screen.

Why do users click? I guess he found a picture on some kind of APP and wanted to check it out. The image must be a RenderObject(otherwise you’ll see a **), so how does the code writer know which image the user clicked on?

Flutter withHitThe interface at the beginning helps us do this, andRenderObjectThere are three interfaces associated.

1. HitTestablethehitTestMethod, let thisRenderObjectCan dot? What is can dot? A laterHitTestResultI’ll tell you.
2. HitTestDispatcherthedispatchEventMethods, well, can fire events.
3. HitTestTargetthehandleEventMethod,RenderObjectCan be clicked, that event you do not deal with, how to deal with, is the content of this method.

High energy to the

Recursively from RenderView’s hitTest, call Child’s hitTest according to the position of the pointer event. RenderView is the root of the RenderTree. See Binding for details.

Super. hitTest specifies the GestureBinding hitTest, and RendererBinding specifies the GestureBinding hitTest. The screenshot corresponds to a RendererBinding. (Does not affect the reading of the following content)

Recursively invoke content resolution

RenderTree Starts hitTest from RenderView. Most of the time, what we create is a RenderBox, a box model that has size information like length and width, and uses a Cartesian coordinate system. Some have a single child, or a double linked list of children. In these cases, different components have different click tests.

Here’s the Stack and Container component to give you a sense of the recursive process.

Stack(
  children: [
    GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: (){print("blue"); }, child: Container(width:300,height: 300,color:Colors.blue)),
    GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: (){print("red"); }, child: Container(width:150,height: 150,color:Colors.red))
  ],
),
Copy the code

Some random BLOG post said that setting the Behavior of the GestureDetector would do clickthrough, whereas clicking on the red block console would only output red.

Why is that? The answer is hitTest.

The Stack is the corresponding RenderStack, is a double linked list of child model (by ContainerRenderObjectMixin implementation). Its hitTest is the method of RenderBox, original.

If the Box contains the click points, hitTestChildren will be hittested one by one, and then hitTestSelf will be hittested if the former fails. As long as it’s in, add yourself to result.

Result stores the RenderBox for all passed tests, which receive pointer events.

RenderStackthehitTestChildren

The hitTest process can be interpreted as starting with lastChild, clicking forward until a child passes, adding result inside the addWithPaintOffset, and then returning true.

Why start with lastChild? Because it is the RenderBox at the top of the stack, this ensures that the top one covers the bottom one, making it impossible for clicks to penetrate normally.

HitTestSelf returns false by default, and subclasses can be overridden as needed. (GestureDetector RenderPointerListener, Listener, RenderBoxWithHitTestBehavior are detailed content, then click through speak)

So the conclusion is, inHitTestChildrenMedium, red squareRenderBoxIt’s addedHitTestResultThe blue box gets no pointer information.

hitTestconclusion

This is a top-down, recursive process, internally implemented primarily by hitTestChildren and hitTestSelf. The click coordinates inside the RenderBox are a prerequisite for hitTest, but hitTest depends on how the component implements hitTest, and whether it receives pointer events depends on whether the RenderBox is added to the HitTestResult.

Curious about the contents of the result, in _handPointEventImmediately printing result.

DispatchEvent Dispatches notifications

The obtained HitTestResult is iterated through the internal path, calling the handleEvents of each RenderObject. In fact, there are also contents such as pointRoute inside, so I haven’t studied them yet.

To interrupt an unrelated message, the handleEvent below the GestureBinding method is the content of the gesture arena. (Gestures are gesture things, Pointers are not gesture things)

conclusion

Therefore, the following three processes are required for platform pointer event delivery.

  1. Wrap pointer data that Flutter can understand

  2. Select the RenderObject that needs to respond to the event

  3. The handleEvent that executes the RenderObject

Click through ????? The next article will cover implementation. Here’s the goo goo.