Flutter Widget In Detail : AbsorbPointer & IgnorePointer
Detailed Explanation of AbsorbPointer & IgnorePointer Widgets
Introduction :
- Have you ever been in a situation in your app where there are multiple buttons, listview, list tiles, cards, containers, etc, and you want to stop interaction/touches from the user from all of those widgets at once?
- Of course, you can pass
null
to the buttonsonPressed
property. But when there are too many buttons in your row/column or any other widget, it is very frustrating to pass this property one by one. - Consider the below example where there are 5 buttons inside
Column
. Now we want to disable all the buttons for some reason. For that, the common way is to go and writenull
to every button'sonPressed
property.Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton(child:Text("1"),onPressed:null), SizedBox(height:5.0), ElevatedButton(child:Text("2"),onPressed:null), SizedBox(height:5.0), ElevatedButton(child:Text("3"),onPressed:null), SizedBox(height:5.0), ElevatedButton(child:Text("4"),onPressed:null), SizedBox(height:5.0), ElevatedButton(child:Text("5"),onPressed:null), ] )
- Isn't it a very boring way to do this? Yes IT IS. And what if we want to disable all the interactions from our app at once ๐ง?
- So what's the solution to this problem? ๐ค
- Well Flutter provided two very useful widgets to solve this problem. The solution is to wrap your widget inside AbsorbPointer / IgnorePointer.
- You can disable the user's whole app interaction by simply wrapping your main widget from the widget tree inside the AbsorbPointer.
- Let's see both the widget one by one and understand what they are doing and what is the difference between them.
AbsorbPointer :
- As the name screaming, AbsorbPointer absorbs the click event. In other words, It prevents click events from the child widget.
- Not only it prevents
click
events, but it also preventsscroll
,drag
,hover
events. Properties :
absorbing
: Whentrue
, The widget prevents its subtree from receiving all kinds of events. Whenfalse
, The widget will allow its subtree to receive the events.child
: Pass widget/widgets from which you want to disable events.ignoringSemantics
: This takes boolean as a parameter.true
means widget should be ignored by screen readers when compiling the semantic tree.- Example :
Stack( alignment: AlignmentDirectional.center, children: <Widget>[ SizedBox( width: 200.0, height: 100.0, child: ElevatedButton( onPressed: () {}, child: null, ), ), SizedBox( width: 100.0, height: 200.0, child: AbsorbPointer( absorbing: true, child: ElevatedButton( style: ElevatedButton.styleFrom( primary: Colors.red.shade200, ), onPressed: () {}, child: null, ), ), ), ], );
- Output :
- Here we can clearly see that the red Box is completely disabled. If you observe the
onPressed
property of the red button, it is not null, but still, it is not receiving any kind of event because we've wrapped our button inside the AbsorbPointer - This is a very small example. But imagine if have too many widgets on your screen like below,
- Then AbsorbPointer is the right and best way to disable everything at once.
IgnorePointer :
- As the name screaming, IgnorePointer ignores/prevents their children's widget from pointer-events as well as the whole widget tree interaction.
- Like AbsorbPointer, IgnorePointer can ignore pointer-events like tapping, dragging, scrolling, and hover.
Properties :
ignoring
: Iftrue
it will ignore the pointer event.child
: Pass widget/widgets from which you want to disable events.ignoringSemantics
: Iftrue
the widget will be ignored when compiling the semantics tree. And will be ignored by the screen readers- Example :
Center( child: IgnorePointer( ignoring: ignoring, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Text('Ignoring: $ignoring'), ElevatedButton( onPressed: () {}, child: const Text('Click me!'), ), ], ), ), ),
- Output :
- As you can see that when we change the value of the
ignoring
fromfalse
totrue
, the button is no longer clickable.
- Hh ๐ค !! So what's the difference between AbsorbPointer and IgnorePointer? Both are doing the same thing right !! But NO there are some differences. Let's see.
- So let's understand the core difference between both of them by taking an example.
Difference Between AbsorbPointer and IgnorePointer :
Example - 1 :
- Consider two boxes
red
box and theblue
box. The red box is on top of the blue box.
Stack(
alignment: Alignment.topCenter,
children: [
BlueBox(
onClicked: () {print("Blue box clicked")}.
child: Container(width: 200, height: 150),
color: Colors.blue,
),
RedBox(
onClicked: () {print("Red box clicked")}.
child: Container(width: 100, height: 100),
color: Colors.red,
),
]
)
- All is working as aspected. Both buttons are working with their individual
onClicked
property.
- Now we are going to wrap the
red
box inside theIgnorePointer
. Let's see what happens IgnorePointer( child: RaisedButton( onPressed: (){print("Red box clicked")}, child:Container( child: Container(width: 100, height: 100), color: Colors.red, ), ) )
Output :
As we can observe that now the
red
box is not clickable anymore. Even if we click on thered
box theblue
box will be clicked.Now let's wrap the same
red
box with the AbsorbPointer and see what happens.AbsorbPointer( child: RaisedButton( onPressed: (){print("Red box clicked")}, child:Container( child: Container(width: 100, height: 100), color: Colors.red, ), ) )
Now you can see that the
red
box is not even showing that click event/hand cursor.- So basically AbsorbPointer is ignoring the interaction of the child widget by also ignoring the widget below it, which here is some part of the
blue
box below thered
box. IgnorePointer is also ignoring the interaction of the child widget. But it will not ignore the widget below it.
Simply put, In AbsorbPointer what is happening is that The
red
box contains some part of theblue
box below it, that's why it is also absorbing/ignoring that part and disable it.But IgnorePointer will not absorb/ignore that below part and it will make the whole box clickable by not considering the
red
box click event
Example - 2 :
- Consider a Button. which is also wrapped inside
GestureDetector
widget.GestureDetector( onTap: (){ print("Gesture Detector"); }, child:ElevatedButton( style: ElevatedButton.styleFrom(primary: Colors.transparent), onPressed: (){ print("Red Button Pressed"); }, child:Container(width: 100, height: 100,color:Colors.red), ) )
- When you click on this box, it will print
Red Button Pressed
as expected. Notice that it will not call theonTap
function of theGestureDetector
widget. - Now let's wrap the
ElevatedButton
inside ourIgnorePointer
.GestureDetector( onTap: (){ print("Gesture Detector"); }, child: IgnorePointer( child: ElevatedButton( style: ElevatedButton.styleFrom(primary: Colors.transparent), onPressed: (){ print("Red Button Pressed"); }, child:Container(width: 100, height: 100,color:Colors.red), ) ) )
- Now what do you think .... what will happen if we click on the red box again ๐ค??
- Will it print the statement of
GestureDetector
orElevatedButton
?? - It will not print any of the statements!!!. Why ?? Because as we've discussed above that
IgnorePointer
will ignore its child as well as its whole widget tree.. It means it will also ignore theGestureDetector
because it is also a part of theIgnorePointer
s widget tree. Now let's wrap our button inside AbsrobPointer.
GestureDetector( onTap: (){ print("Gesture Detector"); }, child: AbsorbPointer( child: ElevatedButton( style: ElevatedButton.styleFrom(primary: Colors.transparent), onPressed: (){ print("Red Button Pressed"); }, child:Container(width: 100, height: 100,color:Colors.red), ) ) )
- As we can see the event of our
ElevatedButton
is ignored but theonTap
of theGestureDetector
is working. Because theAbsorbPointer
will only absorb the child of it. Not the whole widget tree.
That's IT. That's all you need to about AbsorbPointer and IgnorePointer. Hope you understood. ๐
Previous Blog : Widget In Detail - Container