flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.56k stars 27.33k forks source link

Add a widget that always returns false from hitTest but still lets its subtree participate in hit testing #76525

Open gertdrui opened 3 years ago

gertdrui commented 3 years ago

Use case

I've needed to have full screen scrollables - either PageView or CustomScrollView - as the frontmost widget in a Stack that also need to allow passing gestures through to the widgets behind, for example when there is a gap between the elements in the CustomScrollView.

With the options currently available, this is not possible, since all scrollviews use Scrollable, which is hardcoded as HitTestBehavior.opaque.

Currently to make this work I've had to copy the contents of Scrollable and ScrollView or PageView to make my own version just so I could set the HitTest behavior to translucent.

Proposal

I propose to make HitTestBehavior configurable in Scrollable as well as the built-in ScrollViews and PageViews that use it.

HansMuller commented 3 years ago

CC @Piinks

This may have unintended consequences. When more than one overlapping gesture detector tries to recognize gestures that are not completely distinct, the results can be confusing and not what you want.

nt4f04uNd commented 3 years ago

fyi similar proposal https://github.com/flutter/flutter/issues/75099

With the options currently available, this is not possible, since all scrollviews use Scrollable, which is hardcoded as HitTestBehavior.opaque.

this is not true, what actually happens is that hit test is stopped at the first child been hit in stack

Hixie commented 3 years ago

@gertdrui Can you elaborate on your use case? It seems weird, as @HansMuller says, to have taps go "through" a scrolling widget to non-scrolling widgets below it. Maybe a little video showing the UI you have in mind, if you have it built already?

gertdrui commented 3 years ago

@nt4f04uNd Setting it to HitTestBehavior.translucent does work for what I'm trying to achieve because I'm only interested in taps reaching the widgets underneath while dragging is captured by the scrollview.

@Hixie Here's my working example, the elements in the background are interactable while dragging anywhere on the screen controls the scrollable area. video

Hixie commented 3 years ago

Ohhhh, I see. It's "above" the list that's static (in the Y direction, when the list is at zero). Interesting.

One way to do that today would be to have a special sliver that never scrolls, always reports a scroll height of zero, and paints across the entire list, FWIW.

But I see what you're suggesting. It sounds like what you want is a widget like IgnorePointer/AbsorbPointer that answers yes, yes, no to the questions in https://github.com/flutter/flutter/issues/74733#issuecomment-767859584.

That should be relatively straightforward to add. The trickiest part would be documenting it in a way that people understand...

Hixie commented 3 years ago

Actually the trickiest part might be naming the widget.

nt4f04uNd commented 3 years ago

It sounds like what you want is a widget like IgnorePointer/AbsorbPointer that answers yes, yes, no to the questions in #74733 (comment).

would this be done instead of #75099, do you think it's better? i think it's a bit tricky and even with decent explanations in docs people will still open issues where they don't understand what's happening

is it bad idea to hit test all the Stack for instance? or to define any other rules that would allow this to be handled by the framework by default and would not require any additional steps from devs

gertdrui commented 3 years ago

Actually the trickiest part might be naming the widget.

[yes, yes, no] would be the exact opposite of absorb so I guess that would be exude :)

Actually I don't see how this is better than having the Scrollable configurable to translucent.

With translucent I have the option to set some of the items in my scrollable to absorb the pointer, blocking the pointer from going through, while wrapping with ExudePointerâ„¢ would make it impossible to have any children absorb the pointer?

Hixie commented 3 years ago

The problem with having the Scrollable configurable to translucent is that it would mean that if you put any render object widget between the Stack and the Scrollable, e.g. a SizedBox, Container, anything, it would no longer do what you want. I'm not even sure that ListView itself doesn't put a render object widget between its parent and the gesture detector that does the scrolling...

nt4f04uNd commented 3 years ago

@Hixie did you miss the https://github.com/flutter/flutter/issues/76525#issuecomment-783801310?

Hixie commented 3 years ago

No, but I didn't have an answer that wouldn't involve doing a lot of work, so I hadn't answered yet. :-) Whoever fixes this bug is probably the person who will have to do the research to figure out the answers.

nmfisher commented 3 years ago

From what I've pieced together across a few different issues, it's currently impossible to trigger both "down1" and "down2" with the following:

  Stack(children: [
                    Positioned.fill(child:Listener(
                        onPointerDown: (_) {
                          print("down1");
                        },
                        child: Container(color:Colors.transparent))),
                    Positioned.fill(child:Listener(
                        onPointerDown: (_) {
                          print("down2");
                        },
                        child: Container(color:Colors.transparent))),
                  ]),

And this issue would solve that.

If so, then +1 from me.