Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Flutter, how can a positioned Widget feel taps outside of its parent Stack area?

Tags:

A Stack contains MyWidget inside of a Positioned.

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

Since overflow is Overflow.visible and MyWidget is larger than the Stack, it displays outside of the Stack, which is what I want.

However, I can't tap in the area of MyWidget which is outside of the Stack area. It simply ignores the tap there.

How can I make sure MyWidget accepts gestures there?

like image 406
MarcG Avatar asked Jul 16 '18 16:07

MarcG


People also ask

How does positioned widget work in Flutter?

Positioned is a widget that comes built-in with flutter SDK. Positioned does exactly what it sounds like, which is it arbitrarily positioned widgets on top of each other. It is usually used to position child widgets in Stack widget or similar. It only works for Stateless and Stateful widgets.

How does stack work in Flutter?

The stack paints its children in order with the first child being at the bottom. If you want to change the order in which the children paint, you can rebuild the stack with the children in the new order. If you reorder the children in this way, consider giving the children non-null keys.

What is stack overflow in Flutter?

A stack overflow is a type of buffer overflow error that occurs when a computer program tries to use more memory space in the call stack than has been allocated to that stack.


2 Answers

This behavior occurs because the stack checks whether the pointer is inside its bounds before checking whether a child got hit:

Class: RenderBox (which RenderStack extends)

bool hitTest(BoxHitTestResult result, { @required Offset position }) {

    ...

    if (_size.contains(position)) {
      if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
        result.add(BoxHitTestEntry(this, position));
        return true;
      }
    }
    return false;
}

My workaround is deleting the

if (_size.contains(position))

check. Unfortunately, this is not possible without copying code from the framework.

Here is what I did:

  • Copied the Stack class and named it Stack2
  • Copied RenderStack and named it RenderStack2
  • Made Stack2 reference RenderStack2
  • Added the hitTest method from above without the _size.contains check
  • Copied Positioned and named it Positioned2 and made it reference Stack2 as its generic parameter
  • Used Stack2 and Positioned2 in my code

This solution is by no means optimal, but it achieves the desired behavior.

like image 76
Norbert Avatar answered Sep 22 '22 12:09

Norbert


I had a similar issue. Basically since the stack's children don't use the fully overflown box size for their hit testing, i used a nested stack and an arbitrary big height so that i can capture the clicks of the nested stack's overflown boxes. Not sure if it can work for you but here goes nothing :)

So in your example maybe you could try something like that

Stack(
  clipBehavior: Clip.none,
  children: [
    Positioned(
    top: 0.0,
    left: 0.0,
    height : 500.0 // biggest possible child size or just very big 
    child: Stack(
      children: [MyWidget()]
    ),
  )],
);
like image 20
George Chailazopoulos Avatar answered Sep 19 '22 12:09

George Chailazopoulos