In Flutter, what is the difference between AbsorbPointer and IgnorePointer, and which would you use to freeze an email compose form while sending?
answer
- both act at hit testing
- IgnorePointer is invisible to hits
- AbsorbPointer claims the hit itself
- what is painted behind
- focus is not blocked
basics
~20 sBoth stop their children receiving pointers. IgnorePointer is invisible to hit testing, so widgets behind it still get touches; AbsorbPointer is hit itself and stops touches reaching anything behind. Use AbsorbPointer to freeze a compose form while sending.
solid answer
~30 sBoth work during hit testing, so recognizers inside never join the gesture arena. `IgnorePointer(ignoring: true)` removes itself and its subtree from hit testing, so a tap passes through to whatever is painted behind, which suits decorative overlays such as a "Draft saved" banner over a form. `AbsorbPointer(absorbing: true)` reports a hit anywhere in its bounds without asking its children, so nothing inside or behind receives the tap, which suits freezing the compose form while a message sends. Neither blocks keyboard focus, so I add `ExcludeFocus`, and since Flutter 3.8 both remove only pointer-related semantics actions, not labels.
code
dart · 35 linesimport 'package:flutter/material.dart';
class ComposeScreen extends StatelessWidget {
const ComposeScreen({super.key, required this.sending, required this.form});
final bool sending;
final Widget form;
@override
Widget build(BuildContext context) {
return Stack(
children: [
// While sending, taps on the form are absorbed (not passed behind),
// and ExcludeFocus stops keyboard users from editing it too.
AbsorbPointer(
absorbing: sending,
child: ExcludeFocus(excluding: sending, child: form),
),
// A purely decorative banner: taps pass straight through to the form.
const Positioned(
top: 0,
left: 0,
right: 0,
child: IgnorePointer(
child: Padding(
padding: EdgeInsets.all(8),
child: Text('Draft saved'),
),
),
),
if (sending) const Center(child: CircularProgressIndicator()),
],
);
}
}go deeper
Know the one-line difference: IgnorePointer lets touches pass through to what is behind, AbsorbPointer stops them.
Explain that both act at hit testing, before the arena, and what each means for widgets painted behind in a Stack.
Cover the gaps: keyboard focus with ExcludeFocus, the 3.8 semantics change and deprecated ignoringSemantics, and invisible overlays that still hit-test.
Prefer designed disabled states over blanket pointer blocking, so interaction, focus and accessibility stay consistent across the product.
## Two ways to switch touches off Both widgets stop the widgets **inside** them from receiving pointer events, and both do it at **hit-testing** time — before the gesture arena exists. A recognizer that is never hit never joins an arena, so neither widget "cancels" gestures; they simply make sure some recognizers never get a chance. The difference is what happens to the widget **itself** and to whatever is painted **behind** it. | | `IgnorePointer(ignoring: true)` | `AbsorbPointer(absorbing: true)` | |---|---|---| | Children receive pointers | no | no | | The widget itself is hit | no — it is invisible to hit testing | yes, anywhere inside its bounds | | Targets behind it (earlier `Stack` children) | **do** receive the pointer | **do not**, the hit stops here | | Typical use | decorative overlays, badges, labels over interactive content | freezing a form or screen while work is in progress | | Default of the flag | `ignoring: true` | `absorbing: true` | In source terms, `RenderIgnorePointer.hitTest` returns `!ignoring && super.hitTest(...)`, while `RenderAbsorbPointer.hitTest` returns `size.contains(position)` when absorbing — it claims the hit without asking its children. ## Examples in an email app - **Absorb:** while a message is sending, wrap the compose form in `AbsorbPointer(absorbing: sending, ...)`. Taps on the form are swallowed, including taps that would otherwise reach something drawn underneath. - **Ignore:** a "Draft saved" banner painted on top of the form should not steal taps from the field underneath. Wrapping the banner in `IgnorePointer` lets those taps fall through to the field. - A classic bug is the inverse: an invisible overlay (an `Opacity` of 0 or an empty `Container` with a colour) still hit-tests and blocks everything below. `IgnorePointer` around it fixes that. ## What they do not block 1. **Keyboard focus.** Neither widget touches focus. A focused `TextField` or button inside an absorbed form can still be typed into or activated with Enter. Add `ExcludeFocus(excluding: ...)` or disable the controls themselves. 2. **Semantics labels.** Since Flutter 3.8, both widgets remove only the **pointer-related semantics actions** (such as tap) from their subtree while keeping labels and values, so screen readers still describe the content. The old `ignoringSemantics` parameter, which dropped the whole subtree, is deprecated; use `ExcludeSemantics` if that is what you want. 3. **Painting and layout.** Both are visually transparent and size themselves to their child. ## Choosing correctly - Want the region to be a **wall**? `AbsorbPointer`. - Want the region to be **glass** that touches pass through? `IgnorePointer`. - Want to **disable** a single control? Prefer its own disabled state (`onPressed: null`), which also updates visuals and semantics, over wrapping it. - Want to stop one gesture but allow others? That is not a job for either widget; it is arena territory — change which recognizers exist or how they resolve.
- An invisible overlay in a Stack blocks taps on the buttons below it; what is going on?Invisible is not the same as non-hittable. A `Container` with a colour, even fully transparent, or an `Opacity` of 0 over hittable children still participates in hit testing, so the overlay claims the tap before the buttons behind it. Wrap the overlay in `IgnorePointer`, or remove it from the tree when it is not shown.
IgnorePointer turns a pane into clear glass that fingers pass through to what is behind; AbsorbPointer turns it into a wall that stops the finger, with nothing inside or behind it feeling the touch.
saying these in an interview costs you the question
- IgnorePointer and AbsorbPointer are aliases with different names.
- AbsorbPointer lets taps pass through to widgets behind it.
- AbsorbPointer also stops keyboard input to fields inside it.
- IgnorePointer removes its subtree from the accessibility tree entirely.
- They work by rejecting recognizers in the gesture arena.