In a Flutter email list, why does adding onDoubleTap to a message tile make its onTap fire noticeably late?
answer
- someone holds the arena
- sweep deferred while held
- kDoubleTapTimeout is 300 ms
- release runs the pending sweep
- onTapDown then onTapCancel
basics
~20 sDoubleTapGestureRecognizer holds the pointer's gesture arena after the first tap lifts, deferring the sweep that would award the single tap; only after kDoubleTapTimeout (300 ms) passes without a second tap does it release, and onTap fires.
solid answer
~30 sThe arena usually awards an undecided tap on pointer up by sweeping it. When the detector also has `onDoubleTap`, the `DoubleTapGestureRecognizer` calls `hold` on the first pointer's arena, so that sweep is deferred. If no second tap arrives within `kDoubleTapTimeout`, 300 ms, it releases the arena, the pending sweep runs and the single tap wins; if one does, the double tap accepts and the tap is rejected. So every single tap on that tile waits about 300 ms. For a primary action such as opening an email, keep only `onTap` and put starring on a visible button or a long press.
code
dart · 29 linesimport 'package:flutter/material.dart';
class MessageTile extends StatelessWidget {
const MessageTile({
super.key,
required this.subject,
required this.onOpen,
required this.onStar,
});
final String subject;
final VoidCallback onOpen;
final VoidCallback onStar;
@override
Widget build(BuildContext context) {
// Double tap holds the arena, so onTap waits for kDoubleTapTimeout
// (300 ms) before the message opens. Prefer a visible star button:
return ListTile(
title: Text(subject),
onTap: onOpen, // fires on pointer up, no hold
trailing: IconButton(
icon: const Icon(Icons.star_outline),
tooltip: 'Star',
onPressed: onStar,
),
);
}
}go deeper
Remember that adding onDoubleTap makes single taps wait, because Flutter cannot know yet whether a second tap is coming.
Explain hold and release on the arena, the deferred sweep, the 300 ms double-tap timeout, and why onTapDown can end in onTapCancel.
Choose interactions that keep primary taps immediate, keep pressed states symmetric, and prove arena timing with diagnostics when users report laggy taps.
Set interaction guidelines that reserve double tap for places where its latency is acceptable, and prefer visible, accessible controls for secondary actions.
## Holding the arena The gesture arena normally resolves an undecided contest on **pointer up** with a **sweep**: the first member wins. `GestureArenaManager` also lets a member postpone that moment: - **`hold(pointer)`** marks the arena as held. A sweep that arrives while held is **deferred** (`hasPendingSweep`). - **`release(pointer)`** clears the hold; if a sweep was pending, it runs now. Only one recognizer in the framework's common set relies on this: **`DoubleTapGestureRecognizer`**. After the first tap's pointer lifts, it cannot know yet whether a second tap is coming, so it holds the first pointer's arena and starts a timer of **`kDoubleTapTimeout` (300 ms)**. ## Why `onTap` arrives late Give a `GestureDetector` both `onTap` and `onDoubleTap`, and the first pointer's arena contains a tap recognizer and a double-tap recognizer. 1. Pointer up. The double-tap recognizer holds the arena, so the sweep that would award the tap is deferred. 2. If no second tap arrives within 300 ms, the double-tap recognizer gives up and releases; the deferred sweep runs and the tap wins. `onTap` fires roughly 300 ms after the finger lifted. 3. If a second tap arrives in time, the double-tap recognizer accepts, the tap is rejected, and `onDoubleTap` fires instead. In an email list, that means opening a message feels sluggish the moment someone adds "double tap to star". The delay is inherent in the design: the framework cannot report a single tap until it knows it is not the first half of a double tap. ## Why `onTapDown` can be followed by `onTapCancel` A second arena effect confuses people in scrolling lists: - A tap recognizer reports **`onTapDown`** once a short deadline passes (`kPressTimeout`, 100 ms) or once it wins, whichever comes first — even while the arena is still undecided. - If the finger then moves beyond the touch slop, a drag recognizer (the list's scroll) declares victory, the tap is rejected, and the tap recognizer reports **`onTapCancel`**. So pressed-state visuals should start in `onTapDown` and always be undone in both `onTapUp` and `onTapCancel`. `InkWell` does exactly this for its highlight. ## Options for the email tile | Approach | Tap latency | Trade-off | |---|---|---| | `onTap` only | none beyond the sweep | no double-tap shortcut | | `onTap` + `onDoubleTap` | about 300 ms on every single tap | slower opening for every user | | `onTap` + a visible star button | none | uses screen space, but discoverable and accessible | | `onTap` + `onLongPress` for actions | none for taps | long press needs `kLongPressTimeout` (500 ms), but only for the action | A long press does not hold the arena after pointer up; its recognizer competes while the finger is down and wins when the timer fires, so single taps stay immediate. ## Checklist - Avoid `onDoubleTap` on widgets whose single tap is the primary action. - Keep pressed states symmetric across `onTapUp` and `onTapCancel`. - When a tap "sometimes does nothing", log arena resolution with `debugPrintGestureArenaDiagnostics` before blaming hit testing.
- Does onLongPress on the same tile also delay onTap?No. The long-press recognizer competes while the finger is down and wins only if the press lasts `kLongPressTimeout`, 500 ms. If the finger lifts earlier, it has already left the contest and the sweep awards the tap immediately. It never holds the arena after pointer up.
- Why must a pressed-state highlight also be cleared in onTapCancel?`onTapDown` fires after `kPressTimeout`, 100 ms, or on winning, even while the arena is undecided. If a scroll then wins, the tap is rejected and reports `onTapCancel` instead of `onTapUp`, so a highlight cleared only in `onTapUp` would stick.
saying these in an interview costs you the question
- The delay comes from the double-tap recognizer rendering an extra frame.
- onTap fires instantly and onDoubleTap cancels it afterwards.
- A long press holds the arena exactly like a double tap does.
- onTapDown only fires for taps that go on to win.