How to get tap event inside absorb pointer - flutter

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

Related

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

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

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

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.

Flutter - click one RaisedButton and all RaisedButtons update

I am working on a calculator type app that has a lot of buttons on the screen. When I turn on dev tools to check repainting, I noticed that whenever one button is clicked, every button on the screen repaints. Instead of pasting tons of code in here, I created a demo app that shows what it is that I am doing. This is what the screen looks like:
Whenever any of the buttons are pressed, every button on the screen redraws.
This is the code that I'm using:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Button Testing'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
buttonPressed,
textAlign: TextAlign.center,
),
SizedBox(
height: 10.0,
),
RaisedButton(
child: Text('Button One'),
onPressed: () {
setState(() {
buttonPressed = 'Button One Was Pressed';
});
},
),
RaisedButton(
child: Text('Button Two'),
onPressed: () {
setState(() {
buttonPressed = 'Button Two Was Pressed';
});
},
),
RaisedButton(
child: Text('Button Three'),
onPressed: () {
setState(() {
buttonPressed = 'Button Three Was Pressed';
});
},
),
RaisedButton(
child: Text('Button Four'),
onPressed: () {
setState(() {
buttonPressed = 'Button Four Was Pressed';
});
},
),
],
),
),
),
);
}
I've tried breaking the button area out into a separate widget and nothing I've done matters. My question is, is there something else that I should be doing? Is this expected and acceptable behavior? Does it even matter that everything is redrawing whenever any buttons are tapped? I thought that in Flutter, only the things that needed to be redrawn were redrawn.
Whenever you call setState then it rebuild whole build method, so it is obvious that it rebuild RaisedButton.
You can avoid by using provider, Bloc and so on which will not rebuild whole build method. It will only change data.
It is totally depends on you that you want to accept it or not. If widget is not that much big than you can keep it according to me.
Redrawing widget every time is really not good practice but if it is not rebuilding very frequently or your application is not that much big then it is fine.
setState will rebuild all the screen ,
So you can avoid that by using Bloc it is a state controller which won't rebuild everything only what you need to rebuild you can use it from scratch or use the plugin bloc 1.0.0