Flutter - how to ignore parent touch event on child touch event? - flutter

This should be pretty basic, but I can't seem to figure out how to disable or ignore parent touches when a child IconButton is pressed. I am working on a treeview that can expand and collapse. When I click the row (a Card), the children will be shown or not (which is working). The problem I have is that when touching the child IconButton, both the IconButton's onPressed event as well as the Listener of the Card's onPointerDown event. But I only want the IconButton's onPressed event to trigger. Any suggestions?
The listener for the card (row):
Listener(
behavior: HitTestBehavior.deferToChild,
onPointerDown: (PointerDownEvent event) {
toggleExpanded();
print("row pressed");
},
child: widget.parent
),
The IconButton:
return Card(
color: Theme.of(context).colorScheme.surfaceVariant,
elevation: 2.0,
child: ListTile(
leading: Icon(Icons.folder, color: Colors.amber),
title: titleWidget,
subtitle: countWidget,
//trailing: expandButton,
trailing:
IconButton(
icon: Icon(Icons.more_vert), onPressed: () { print("more icon"); },
),
),
);

Use GestureDetector instead
GestureDetector(
onTap: () { print("row pressed"); },
child: <widget>
),

Related

have search bar handle one tap and two taps differently

I am pretty new to flutter/dart, so this might be a silly question...
I wanted to have a search bar, that when tapped the first time, displays a series of listview tiles (like pre-canned search terms). I wanted a subsequent tap to then open the soft keyboard for user input. As it stands now, a single tap opens the listview tiles, and also opens the soft keyboard.
After some looking around, I am thinking I would need to wrap the searchbar in a GestureDetector, and handle the tap / double-tap gestures through that. What I can't quite figure out, is how to tie the GestureDetector gestures, ontap and ondoubletap, to the child widget actions... I think when the searchbar gets focus (onTap), the soft keyboard opens, so not sure if that behavior can be (easily) uncoupled...
The flutter cookbook example uses this:
ScaffoldMessenger.of(context).showSnackBar(snackBar);
but the API docs say this only manages snackbars and MaterialBanners:
"Manages SnackBars and MaterialBanners for descendant Scaffolds."
Here is the framework I have so far:
Widget searchBar() {
//final FloatingSearchBarController searchBarController = FloatingSearchBarController();
return
GestureDetector(
onTap: () =>{},
onDoubleTap: () => {},
child: FloatingSearchBar(
controller: searchBarController,
hint: "search",
openAxisAlignment: 0.0,
width: 600,
axisAlignment: 0.0,
scrollPadding: const EdgeInsets.only(top: 16, bottom: 20),
elevation: 4.0,
onQueryChanged: (query) {
},
builder: (BuildContext context, Animation<double> transition) {
return ClipRRect(
child: Material(
color: Colors.white,
child: Container(
color: Colors.white,
child: Column(
children: [
ListTile(
title: const Text('Item 1'),
onTap: () {},
),
ListTile(
title: const Text('Item 2'),
onTap: () {},
),
ListTile(
title: const Text('Item 3'),
onTap: () {},
),
],
),
),
)
);
},
)
);
Any thoughts would be greatly appreciated!

How to get tap event inside absorb pointer

I have a page containing many widgets which is opened in view only mode. i used AbsorbPointer for ignoring user inputs. but in some cases i have to get onTap event on one button which is inside AbsorbPointer. how can i achieve that ?
I tried using GestureDetector but its not giving onTap event.
any idea??
AbsorbPointer(
absorbing:true,
child: Column(children: [
Text("HELLO"),
Text("HELLO 2"),
GestureDetector(
onTap: () {
showToast("I tried this");
},
child: IconButton(
icon: Icon(
Icons.local_offer,
color: Colors.red,
),
onPressed: () {
print("I need this event");
},
),
)
]),
);
use absorbing: property for your cases... set it to false in the case you want to allow the GestureDetector inside

how to make tap to open slidable using flutter_slidable package?

to achieve this
currently I use this code below
Slidable(
child: ProductListItem(product: product),
endActionPane: ActionPane(
motion: const StretchMotion(),
children: [
SlidableAction(
onPressed: (context) {
//
},
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
icon: Icons.edit,
label: "edit".tr(),
),
SlidableAction(
onPressed: (context) {
//
},
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: Icons.delete,
label: "delete".tr(),
),
],
),
),
I need to swipe in the right side to open that endPaneActions.
what I want is ....
if I tap that list item, then it will programmatically open that endPaneActions. how to do that?
from the tutorial on Youtube in here , it seems I can use the code below
final slidable = Slidable.of(context);
final isClosed = slidable.renderingMode == SlidableRenderingMode.none;
if (isClosed) {
slidable.open();
}
but it seems that code is obsolete, I can't find .renderingMode method on version 1.2.0
what is the latest version to programmatically open 'end actions pane' ?
finally I can make tap to open slidable programmatically on version 1.2.0. I got a clue from austin's answer on Github
Slidable(
child: LayoutBuilder( // <---- add LayoutBuilder
builder: (contextFromLayoutBuilder, constraints) {
return GestureDetector( // <--- add Gesture Detector
child: YourListItem(), // <--- your list item in here
onTap: () {
final slidable = Slidable.of(contextFromLayoutBuilder);
slidable?.openEndActionPane(
duration: const Duration(milliseconds: 500),
curve: Curves.decelerate,
);
},
);
},
),
endActionPane: ActionPane( // <--- I use endActionPane in here
motion: const StretchMotion(),
children: [],
),
)
as you can see, I add LayoutBuilder and GestureDetector inside the Slidable widget. LayoutBuilder is used to get the 'latest' context inside the Slidable widget, so the slidable value will not be null.
NOTE:
I use endActionPane here, so I use openEndActionPane method. if you use startActionPane, then you should use openStartActionPane method instead

How to make InkWell `onLongPress()` override tap on a TextField inside of it?

I have a TextField inside of an InkWell with an onLongPress() callback. The problem is, despite the fact that even when long pressing on the TextField, I see the ripple effect on InkWell, but the onLongPress() does not run after the long press time passes. It only gets me into editing Text. When pressing on the bottom side of the Card, everything runs fine.
In short: On tap I want to get into TextField editing. On long press I want to trigger the onLongPress(), not the TextField, even if I am pressing on it.
How do I do this? Thank you.
InkWell(
onLongPress: () {
// do stuff
}
child: ListTile(
title: TextField(),
),
),
You can use the AbsorbPointer widget to ignore the TextField gesture recognizer:
InkWell(
onLongPress: () {
print('onLongPress');
},
child: AbsorbPointer(
child: ListTile(
title: TextField(),
),
),
)
To still enabling the editing of TextField when single tapping on it, you can use FocusNode like this:
InkWell(
onLongPress: () {
print('onLongPress');
},
onTap: () => node.requestFocus(),
child: AbsorbPointer(
child: ListTile(
title: TextField(
focusNode: node,
controller: textController,
),
),
),
)
#Bach 's answer helped me to find a solution. Thank you!
InkWell(
onLongPress: () {
// do stuff
},
child: ListTile(
title: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_focusNode),
child: AbsorbPointer(
child: TextField(
focusNode: _focusNode,
),
),
),
),
The only problem is now that I started messing with focusNode, multiple input fiels are focusing at the same time. But that is a whole other story ;)
UPD: Just realised, that I can't move text cursor this way. So not useful.
It seems that IntrinsicWidth widget can find the right balance between long press and text editing.
The rationale behind is that IntrinsicWidth will let the TextField shrink to its minimum width, therefore avoiding a gesture collision with the InkWell
So your solution can be like this:
InkWell(
onLongPress: () {
// do stuff
}
child: ListTile(
child: IntrinsicWidth(
title: TextField(
//remember to make some hints here
//because with intrinsicwidth if your textfield is empty it might disappear
),
),
),
),

Using a textfield as a button

I'm trying to use a textfield as a button so when someone taps it, it takes him to a different page.
I've tried disabling the textfield but then I cannot use the onTap property. But if the textfield is not disabled, when I click it, it gets editing focus and this is something I don't want.
Can anyone help what's the easiest way this could be done?
Thanks
TextField(
readOnly: true,
onTap: () {
// Do something
},
);
You can use a Flatbutton instead. Just put this in your code:
FlatButton(
onPressed: () {
/*...*/
},
child: Text(
"Clickable text!",
),
)
Or else use a GestureDetector:
GestureDetector(
OnTap: () {...},
child: TextField(
// Text...
),
);
you can wrap it with an InkWell, see below:
InkWell(
onTap: () {
},
child: Container(
padding: EdgeInsets.all(4.0),
child: Text('Click me'),
),
);
you can try the following.
GestureDetector(
onTap: () {
// redirect // nav to different screen
},
child: TextField(
enabled: false
),
)
References
TextField
GestureDetector