skip to content

In Flutter, what is the difference between AbsorbPointer and IgnorePointer, and which would you use to freeze an email compose form while sending?

level: juniorimportance: should knowfreq 44%

answer

  1. both act at hit testing
  2. IgnorePointer is invisible to hits
  3. AbsorbPointer claims the hit itself
  4. what is painted behind
  5. focus is not blocked

basics

~20 s

Both 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 s

Both 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 lines
dart
import '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

for a junior

Know the one-line difference: IgnorePointer lets touches pass through to what is behind, AbsorbPointer stops them.

for a middle

Explain that both act at hit testing, before the arena, and what each means for widgets painted behind in a Stack.

for a senior

Cover the gaps: keyboard focus with ExcludeFocus, the 3.8 semantics change and deprecated ignoringSemantics, and invisible overlays that still hit-test.

for a principal

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.