How to skip Flutter Widget in tap detection? - flutter

I'm trying to make a modal bottom sheet with ListTiles that contain a Checkbox as the leading widget. However, the Checkbox will receive tap event instead of the ListTile, now showing the ink ripple effect for the tile and making me implement two onTap/onChanged callbacks.
I have tried not implementing the Checkbox's onChanged callback and wrapping it around an AbsorbPointer widget, however in these two cases the ListTile would also not get the tap event.
return ListTile(
onTap: onTap,
title: Text("Show expired schedules"),
leading: Checkbox(value: snapshot.data, onChanged: (_) => onTap()),
);
I would like to somehow make the Checkbox not tappable, but have the ListTile still receive the tap event. Kind of like AbsorbPointer, except skipping one Widget, not absorbing the tap event entirely.

Wrap your Checkbox widget inside IgnorePointer widget and it should work:
return ListTile(
onTap: onTap,
title: Text("Show expired schedules"),
leading: IgnorePointer(child: Checkbox(value: snapshot.data, onChanged: (_) => onTap())),
);

You can do that using AbsorbPointer too
return ListTile(
onTap: onTap,
title: Text("Show expired schedules"),
leading: AbsorbPointer(
absorbing: _condition, // bool value, true makes it absorb touch event on CheckBox still making ListTile tappable.
child: Checkbox(value: snapshot.data, onChanged: (_) => onTap()),
),
);

Related

inkwell ontab does not work when clicking on the form

I use the searchField, judging by the documentation, there should be an onTab field in the searcField widget, but apparently the library has been updated and there is no such function anymore, since I need to trigger an event in the block when clicking on the field, I decided to wrap the widget in inkwell, but when clicked, nothing works and the event does not called
BlocBuilder<FillprofileBloc, FillprofileState>(
builder: (context, state) {
return InkWell(
focusColor: Colors.transparent,
onTap: () {
_bloc.add(OnCitySearchTabEvent());
},
child: SearchField(
searchInputDecoration: InputDecoration(
There is a workaround with GestureDetector.
GestureDetector(
behavior: HitTestBehavior.translucent,
onPanDown: (_) {
debugPrint("pan down");
},
child: SearchField(),
),
You can use onTap param of TextField itself here. Or wrap your TextField with IgnorePointer to use InkWell

How to disable clicks detection in a CheckBox that's wrapped by an InkWell in Flutter

I have those 4 Inkwell(), inside each of them is a CheckBox(),
In the onTap: property of the InkWell() I assigned an unnamed function that does some logic.
When I click on the checkbox, the 'onTap' property of the parent InkWell isn't invoked.
What I wanna do, is when I click on the CheckBox, the onTap of the parent widget InkWell get called, so it's like disabling the CheckBox onChanged property, and letting it just like an unclickable widget.
Note: By assigning null to the onChanged property of CheckBox, the widget still absorb the click, so the onTap property of InkWell doesn't get called.
Here is my code:
InkWell(
onTap: () { /* DO SOMETHING */},
child: Row(
children: [
SizedBox( /* The flag icon */ ),
Text( /* The language name */ ),
Checkbox(
onChanged: null,
value: (selectedLanguage == widget.id) ? true : false,
)
],
),
)
You can wrap your Checkbox widget inside an IgnorePointer
IgnorePointer(
child: Checkbox(
value: selectedLanguage == widget.id,
onChanged: (v) {
// This won't get called
},
),
)

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
),
),
),
),

Removing keyboard focus on multiple buttons pressed

I'm trying to hide keyboard when tapped everywhere outside of textField. So I wrapped Scaffold with GestureDetector and set onTap with unfocused(). That works well however when a button is pressed then the keyboard is still active
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
actions: <Widget>[FlatButton(child: Text('Done'), onPressed: () {})],
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FlatButton(
child: Text('something'),
onPressed: () {},
),
TextField(),
],
),
),
);
}
Is there any way to remove the focus without adding that unfocused in onTap of all buttons.. Reason is I've got many buttons there and some has even onLogTap set so there would be a lot of duplicate codes
You need to also add code for hide keyboard inside onPressed() method of FlatButton
FlatButton(
child: Text('something'),
onPressed: () {
FocusScope.of(context).unfocus();
},
),
was hoping for some solution where I wouldn't need so many duplicate codes to do one thing.
AFAIK that is not possible because the click event of GestureDetector widget and click event of FlatButton both are different,
You are registering different/separate click event of FlatButton that's why your keyboard is not hiding when you click on FlatButton
Now the reason why your keyboard not hiding when pressed on buttons
Because the click event of your GestureDetector widget if overridden by the click event of your FlatButton
SOLUTION
You can do one thing, create a common method to hide the keyboard, and call that method to from button click
By thinking a little outside of the box I have managed to hide keyboard on all taps by modifying GestureDetector..
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: (pd) {FocusScope.of(context).unfocus();}, //<- replaced
child: Scaffold(
appBar: AppBar(
actions: <Widget>[FlatButton(child: Text('Done'), onPressed: () {})],
),
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FlatButton(
child: Text('something'),
onPressed: () {},
),
TextField(),
],
),
),
);
}
Now the keyboard will hide on taping everywhere outside of the TextField even on Buttons click.. No need to hide it in each button click. Please if you know about better solution then let me know
UPDATE:
This solution will create exception when tap on already focused TextField

Flutter disable touch on the entire screen

Is there any way to prevent my screen from receiving touch events, I don't want to disable touch for every Widget in my app. I just want to lock the app so that it doesn't receive touch events. How can I do that?
You can wrap your widget in AbsorbPointer and it won't receive touches. To enable the touch again, you can set absorbing: false
AbsorbPointer(
child: YourWidget(...),
);
Two way to do :
AbsorbPointer
IgnorePointer
Check difference with example here :
https://programmer.help/blogs/the-difference-between-flutter-absorbpointer-and-ignorepointer.html
Flutter AbsorbPointer vs IgnorePointer difference
Lets see a practical example of using IgnorePointer widget.
This case is pretty common when we started trying to implement something like toggling a selection on a widget to delete or something like this.
RESULT:
Example Senario :
Holding on a WhatsApp message and delete option coming on top. if tap anywhere else while this option active, it will go.
I implemented it like this.
appBar: AppBar(
title: Text('MyApp'),
actions: [
if (_selected != null) // <-- Delete button only appear something selected.
IconButton(
icon: Icon(Icons.delete),
onPressed: () {
// Delete Code here
}
]
),
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
print('Tapped');
setState(() { _selected = null });
},
child: IgnorePointer(
ignoring: _selected != null ? true : false, // <-- Ignore only when selecting something.
child: Column(
children: [
...
// This is a sample message
GestureDetector(
onLongPress: () {
setState(() { _selected = messageId });
}
child: Container(
child: Text('This is a message'),
),
...
Hope it will help somebody! Have a nice day.