Skip to content

Document that widgets in the overflow of stack do not respond to gestures #19445

Closed

Description

@marcglasberg

Internal: b/143100923

When a Stack contains MyWidget inside of a Positioned.

    Stack(
      overflow: Overflow.visible,
      children: [
        Positioned(
        top: -50.0,
        child: MyWidget(),
      )],
    );

Since overflow is Overflow.visible, MyWidget displays outside of the Stack. However, it doesn't accept gestures in the overflowed area. I have no idea if it's easy, difficult or downright impossible to make this change, but I can think of a gazillion use cases for that, which at the moment must be satisfied by using the much more complex Overlay, or some Stack tricks.

And in case it's impossible to make this change, then at least the documentation should clearly state that overflowed content can't receive gestures.

Also, see: https://stackoverflow.com/questions/51366761/in-flutter-how-can-a-positioned-widget-feel-taps-outside-of-its-parent-stack-ar

Activity

  1. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    d: api docsIssues with https://api.flutter.dev/
    on Jul 17, 2018
  2. added
    customer: crowdAffects or could affect many people, though not necessarily a specific customer.
    on Jan 4, 2019
  3. goderbauer commented on Jan 7, 2019

    @goderbauer
    Member

    It is intentional that widgets in the overflow area of a stack do not react to gestures and that's unlikely to change. I recommend that people with this (or similar) problems refactor their app to keep all interactable elements within the bounds of a stack.

    I do realize that we are not properly documenting this behavior of the stack widget in the API docs. Let's turn this bug into a documentation issue to improve that and properly document the gesture behavior of stack on the class.

  4. changed the title [-]Overflowed positioned widget inside of Stack should be clickable, or documentation updated.[/-] [+]Document that widgets in the overflow of stack do not respond to gestures[/+] on Jan 7, 2019
  5. abacaj commented on Mar 8, 2019

    @abacaj

    Has anyone been able to achieve the same desired effect?

    I need a stack where one widget slides up behind another and it has a gesture for pressing on it.

  6. abacaj commented on Mar 8, 2019

    @abacaj

    It is intentional that widgets in the overflow area of a stack do not react to gestures and that's unlikely to change. I recommend that people with this (or similar) problems refactor their app to keep all interactable elements within the bounds of a stack.

    I do realize that we are not properly documenting this behavior of the stack widget in the API docs. Let's turn this bug into a documentation issue to improve that and properly document the gesture behavior of stack on the class.

    @goderbauer Do you know a way around this? it seems like a bug otherwise how can we have one widget on top of another and the one behind it has a tap detector?

  7. lifenautjoe commented on Mar 13, 2019

    @lifenautjoe

    So the answer to this is to formalise the issue by documenting it?

  8. frederickcxa commented on Mar 20, 2019

    @frederickcxa

    Hi guys, I also hit the same rock with an ongoing redesign in our app.

    We used an OverlayEntry to display the elements that were overlapping in the Stack. You need to get the position + size of the element which you want to put the other widgets on top of, with that you can position the OverlayEntry using those coordinates.

    Doing this, you can get the same position you were doing with the Stack, but now your elements that are overlapping can handle gestures.

    The only problem with this is that the overlay is fixed to those coordinates in the device's viewport. For a scrolling page, the overlay will be floating around, to solve this we used CompositedTransformTarget and CompositedTransformFollower, wrapping the target element with the former and the overlapping widget with the latter, both accept a link: LayerLink property, that lets you linked them, with this your overlay will follow the target widget while you scroll.

    Something like this:

    final _layerLink = LayerLink();
    ...
    return CompositedTransformTarget(
          link: _layerLink,
          child: Text('Overlay target'),
        );
    ...
    _overlay = OverlayEntry(builder: (_) {
            final RenderBox renderBoxRed = context.findRenderObject(); // BuildContext of the target element
            final position = renderBoxRed.localToGlobal(Offset.zero);
            final size = renderBoxRed.size;
    
            return Positioned(
              left: 16, // Horizontal positioning
              right: 16,
              child: CompositedTransformFollower(
                link: _layerLink,
                offset: Offset(0, (position.dy + size.height)), // Vertical positioning using the target dimensions
                child: Text('Overlay follower...'),
              ),
            );
          });
    }
    
    Overlay.of(context).insert(_overlay);

    Hope it helps, if you want to know more about those widgets and different approaches on how to get a widget's size and position, check out these great articles that guided me to this approach.

    Flutter: Using Overlay to display floating widgets
    Flutter : Widget Size and Position

  9. Skyost commented on Apr 9, 2019

    @Skyost

    The problem with all of this is when I want to position an element like this :

    Positioned(
      top: 0,
      left: 0,
      child: GestureDetector(
        onTap: () => Navigator.pop(context),
        child: Icon(
          Icons.arrow_back_ios,
          color: Colors.white,
          size: 32,
        ),
      )
    )

    Well it works fine on Android, iPhone, ... Except the iPhone XS Max (because of its rounded corners a part of this widget becomes invisible and it becomes unclickable).

  10. genesiscz commented on May 10, 2019

    @genesiscz

    It is intentional that widgets in the overflow area of a stack do not react to gestures and that's unlikely to change. I recommend that people with this (or similar) problems refactor their app to keep all interactable elements within the bounds of a stack.

    I do realize that we are not properly documenting this behavior of the stack widget in the API docs. Let's turn this bug into a documentation issue to improve that and properly document the gesture behavior of stack on the class.

    So what is the way to get the gesture when you have a positioned widget to be out of bounds of a container then?

  11. thenexus00 commented on May 13, 2019

    @thenexus00

    Agreed, Because you are going to have lots of cases with design where you have a big button sitting at a bottom of a square which is a button and so on. To say you can not because it is instead cuts out a lot of designs.

  12. localpcguy commented on Jun 6, 2019

    @localpcguy

    This seems like something that should be a choice for the developer, not a limitation of the framework. I'm ok if the default is how it works currently, but an option to allow the click to allow for interaction outside of the stack boundaries would give that choice to the developer and allow for more options.

    I was trying to implement a TapOutside widget that blocks interaction with other elements on the screen when a TextField is focused, and clears focus when tapped (and then allows interaction again). That's when I ran into this limitation. I don't think I can use a ModalRoute, as I want the user to click on and use a TextField that exists in the current screen (not push them to a new route).

    I don't want to get too much more specific on the use case or ask for help (although if there is a "Flutter way" that I'm not seeing, I would potentially withdraw my complaint), just trying to demonstrate that there could be a use case. I've also looked into adding a GestureDetector at the root of the screen, and while that "works" for clicks not on other elements, because it's at the root, clicks on it do not block clicks on other elements on the screen. Maybe something with RawGestureDetector would work, but that seems like an awfully complex solution when putting a transparent barrier over the elements when the widget is focused is very simple. It just doesn't work due to this issue.

  13. adriancmurray commented on Jun 12, 2019

    @adriancmurray

    I agree that this should be left up to the developer. This is the second time I've come across this issue. My first time around I found a decent enough work around, now, it's incredibly verbose to get what I am aiming at when really this should just be left up to the developer to decide, not necessarily a design limitation of the framework.

  14. goderbauer commented on Jun 13, 2019

    @goderbauer
    Member

    I understand the disappointment. Unfortunately, performing hit-testing outside of the bounds of a stack in the overflow is prohibitively expensive in terms of performance.

    I do still believe that you can always restructure your app to not require hit-testing in the overflow of a stack. Maybe, people can post some runnable code examples of where you'd like to do hit testing in the overflow of the stack and we can figure out how to restructure the code to not require that. Those example uses cases would also be good for us to include in the updated docs of overflow.

  15. 48 remaining items

  16. Akifcan commented on Dec 5, 2020

    @Akifcan

    move clickable widget to bottom side like this

    child: Stack(
              children: [
                ListTile(title: _productImage(context), subtitle: _productInfo),
                Positioned( //clickable widget
                  right: 20,
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        icon: Icon(Icons.shopping_basket),
                        onPressed: () {
                          context.read<User>().addFirstItemToBasket(product);
                        },
                      ),
                      Text('0'),
                      IconButton(
                        icon: Icon(Icons.add),
                        onPressed: () {},
                      ),
                      IconButton(
                        icon: Icon(Icons.remove),
                        onPressed: () {},
                      ),
                    ],
                  ),
                ),
              ],
            ),
    
  17. IlyaMax commented on Dec 29, 2020

    @IlyaMax
  18. leegohi04517 commented on Apr 25, 2021

    @leegohi04517

    why closed this issue

  19. snoopdoggy322 commented on Apr 27, 2021

    @snoopdoggy322

    Please fix this issue 🙏🏻

  20. esDotDev commented on Apr 27, 2021

    @esDotDev

    This is the best bet for getting a fix for this anytime soon, please share your use cases here:
    #75747

  21. yasinarik commented on May 29, 2021

    @yasinarik

    Why is this issue still considered as "works as expected" or "works as intended"?

    Clearly, an undeniable amount of power users of Flutter facing it.

  22. AliYar-Khan commented on Jun 7, 2021

    @AliYar-Khan

    gesture detector is showing weird behavior... for one Screen it is working outside the stack and and for other screen it is not working.
    so i solved this using a container with padding top or bottom and color transparent... this way gesture detector detect tap as it comes inside of stack area and is clickable

    code example will be appreciated

  23. Hwan-seok commented on Jul 3, 2021

    @Hwan-seok

    I faced the same issue now and resolved it as follows.
    This can not apply to every case but some cases would be solved.
    In my case, I need to overlap half of the tappable widget to the end of the bigger container.

    The key is Make a Stack bigger than you need and locate tappable positioned widget at the end

    Minimum reproduction codes:

    SizedBox(
                  width: dashboardContainerWidth,
                  height: dashboardCenterContainerHeight + 50, // <-- Originally needed Stack size + tappable widget's height
                  child: Stack(
                    clipBehavior: Clip.none,
                    alignment: Alignment.center,
                    children: [
                      Align(
                        alignment: Alignment.center,
                        child: Container(
                          height: dashboardCenterContainerHeight,
                          decoration: BoxDecoration(
                            color: AppColors.light_gray,
                            borderRadius: AppBorderRadius.circularMax,
                            border: Border.all(),
                          ),
                        ),
                      ),
                      Positioned( 
                        top: 0,
                        child: OrangeArrowButton( < -- height is 50, tappable widget
                          arrowIcon: AppIcons.arrow_upward,
                        ),
                      ),
                      Positioned(
                        bottom: 0,
                        child: OrangeArrowButton(< -- height is 50, tappable widget
                          arrowIcon: AppIcons.arrow_downward,
                        ),
                      ),
                    ],
                  ),
                ),

    Result UI of mine:
    image

    Turn on Widget Inspector:
    image

  24. mitchellsion commented on Jul 4, 2021

    @mitchellsion

    The key is Make a Stack bigger than you need and locate tappable positioned widget at the end

    Wrapping Stack with SizedBox worked for me

    SizedBox(
      width: double.infinity, // or `height: double.infinity` based on your case
      child: Stack(
        children: [
          ...
        ],
      ),
    )
  25. adschi commented on Jul 16, 2021

    @adschi

    Any updates on this? Got a production app and really need this feature.

  26. leegohi04517 commented on Jul 19, 2021

    @leegohi04517

    why closed the issue???

  27. marcglasberg commented on Jul 25, 2021

    @marcglasberg
    Author

    Hey everyone, this issue was closed because it's now only asking for the documentation to clearly state the limitation.

    This documentation change was actually done by #60726

    Please note, there are other issues that could solve this, here:

    And there is also this package:

  28. github-actions commented on Aug 9, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  29. locked as resolved and limited conversation to collaborators on Aug 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

a: annoyanceRepeatedly frustrating issues with non-experimental functionalitycustomer: crowdAffects or could affect many people, though not necessarily a specific customer.d: api docsIssues with https://api.flutter.dev/f: gesturesflutter/packages/flutter/gestures repository.frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onwaiting for PR to land (fixed)A fix is in flight

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions