I have found that GestureDetector can have different hit test behaviours:
deferToChild to work on non-empty space
opaque and translucent to work on all space
But how can I make GestureDetector work only on empty space?
GestureDetector(
onTap: fallbackHandler, // called only when no children hit
child: Stack(
children: manyOtherGestureDetectors, // should keep working, not blocked by the parent
),
)
One solution could be putting the GestureDetector at the bottom layer of a Stack:
Stack(
children: [
GestureDetector(
onTap: fallbackHandler,
),
...manyOtherGestureDetectors,
],
)
But it becomes hard to size (shrink) the backgourd GestureDetector as it is no longer a parent. One way to shrink it is to use IntrinsicHeight, but IntrinsicHeight does not work with TextField.
So is it possible to create a GestureDetector that:
Is behind (blocked by) all other GestureDetectors;
Shrinks its size to exactly wrap all other GestureDetectors?
Not sure on this, make a try.
You can use the IgnorePointer widget to block any gesture for it's child.
Wrap the child with IgnorePointer and make ignoring: true,
Ex: GestureDetector(
onTap: fallbackHandler, // called only when no children hit
child: IgnorePointer(
Stack(
children: manyOtherGestureDetectors, // should keep working, not blocked by the parent
),
`ignoring: true,`
),
)
Related
I'm trying use IndexStack in my app. But I have a problem with Stack and ElevatedButton. I trying press button and it don't response anything to me. But the blue button bellow is still working well
Thank for concern about my issue.
Codepen: https://codepen.io/sucanabo/pen/xxqPrRw
How Stack widget works, is that it takes the first child from the children list that you provide it and makes it the base of the rendering.
Whatever children you provide it after the first child, will be rendered on top of the base child.
So, when your Positioned child is becoming the base, your ListView child (which is the second in the children list) is being rendered on top of your Positioned widget, which is hence blocking all the interaction.
If you want this to work with your architecture, move the ListView as the first child and your Positioned as the second child, like this,
Stack(
clipBehavior: Clip.none,
children: [
// First the ListView
ListView(
padding: EdgeInsets.fromLTRB(20.0, 50.0, 20.0, 20.0),
children: [],
),
// Then the Positioned
Positioned(
top: -35.0,
left: 0,
right: 0,
),
],
),
Is it possible to detect, for example, a tap event on a certain Widget that is inside an IgnorePointer or AbsorbPointer in Flutter?
Code example:
AbsorbPointer(
absorbing: *some condition to make it true or false*,
child: ListView(
children: [
SomeButton(
onTap: () => print("i want this method to run when this button is tapped even if absorbing is true"),
),
...
],
),
),
Just out of curiosity why are you using a widget just to take the main functionality of that widget away I'm just curious? So the answer for AbsorbPointer is yes you can have a widget wrapped in AbsorbPointer and RawGestureDetector then define your own gesture factory to override rejectGestures so it accepts all gestures then theGestureArena will still register taps. For IgnorePointer this setup will not work. I cannot figure any setup that overrides IgnorePointer.
SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
//Column Content
],
),
),
You could also wrap AbsorbPointer in Listener because this doesn't use the GestureArena if you want to read more about this Nash wrote a great article about it Flutter Deep Dive: Gestures
I'm using GesturedDetector + Container in ListView as children, and it works fine.
but sometimes it's hard to scroll if I touch the Children when scroll.
Because GestureDetector detects my scrolling as tap, so the whole ListView doesn't scroll.
If I change the onTap to onLongPress, it solved, but not what I want.
So, Is there any great solution to my situation?
Now I only have an idea that makes children's margin bigger, reducing the chance that children are being touched.
I've tried the following code to try to reproduce your issue but it doesn't happen. When I drag the ListView moves up and down, when I tap it prints the String. Try this code on your app to see if the issue also happens.
ListView(
children: List.generate(50, (index) {
return GestureDetector(
behavior: HitTestBehavior.translucent, // You can try adding this to help
onTap: () => print('item $index'),
child: Container(
alignment: Alignment.center,
child: Text('item $index'),
height: 40,
),
);
}),
)
I have a tree structure like the following, basically a Row inside a SingleChildScrollView, where the children of the Row are Draggables.
Positioned(
top: 250,
left: 30,
child: Container(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: initialState.map((f) => LongPressDraggable(
child: f,
feedback: Transform.scale(scale: 0.4, child: f),
childWhenDragging: Container(),
)).toList(),
),
),
),
),
The problem is that the Draggable seems to be consuming any gesture, so that, when tapping (long press in this case), the drag effect works, but no other gesture seems to work, so basically scroll does not respond.
I tried using, instead of LongPressDraggable, a regular Draggable and use affinity property, but even with that, the scroll does not respond, just the drag.
Any help or suggestion?
For anyone else dealing with this (at the end stupid situation)....
It turns out that the problem does NOT come from the Draggable itself.....
For some reason, putting the SingleChildScrollView inside a Positioned prevents this from scrolling....
Found a clue in this bug: bug report
So that, the solution proposed in the bug about giving double values to the top, bottom, left, right properties of the Positioned did not work well for me.
So...I solved it by using exactly the same code, but using Align widget instead of Positioned.
I am using the Align widget to place an Icon button at the bottom center of the screen.
However, I get the following error and I'm unable to resolve it:
The specific widget that could not find a Material ancestor was:
IconButton
My code:
return Stack(
children: <Widget>[
Container(
child: GoogleMap(
initialCameraPosition:
CameraPosition(target: LatLng(1,1), zoom: 15),
onMapCreated: (map) {
mapReady;
},),
),
Align(
alignment:Alignment.bottomCenter,
child: IconButton(
icon: Icon(Icons.next_week), onPressed: (){}),
)
],
If I replace the IconButton widget with for example a Text widget it works well.
Could you please explain why it doesn't work, why the IconButton needs a Material ancestor?
Because as per documentation of IconButton (https://api.flutter.dev/flutter/material/IconButton-class.html)
An icon button is a picture printed on a Material widget that reacts
to touches by filling with color (ink).
[..]
Requires one of its ancestors to be a Material widget.
IconButton uses most likely ThemeData as well as other things which MaterialApp normally provides.
Is there a reason you dont use MaterialApp as ancestor?
If you wrap you stack (or the parent in general) with Scaffold you will n get this error.
In this case, if you wrap your IconButton with Material Widget,i believe it will fix the problem:
Align(
alignment: Alignment.bottomCenter,
child: Material(
child: IconButton(icon: Icon(Icons.next_week), onPressed: () {})),
)