Files
chanora/apps
EdisonJwa 0c1fd1c2e3 fix(ui,ios): use TextField.onTapOutside instead of outer GestureDetector (fixes _dependents.isEmpty assert + keyboard lag)
User reported 'package:flutter/src/widgets/framework.dart line 6268
_dependents.isEmpty is not true' assertion AND the persistent
keyboard lag.

Root cause analysis:

framework.dart:6268 is the assertion in
InheritedElement.debugDeactivated() that fires when an
InheritedElement is being deactivated while descendants still
depend on it. This fires in debug builds; release builds skip it.

The previous tap-outside-to-dismiss implementation used:

  return GestureDetector(
    behavior: HitTestBehavior.opaque,
    onTap: () => FocusScope.of(context).unfocus(),
    child: Column(...),
  );

The FocusScope.of(context) call subscribes this GestureDetector's
Element to the _FocusScopeMarker InheritedWidget on every build.
During the modal-sheet-pop / Navigator-deactivate sequence, the
ancestor _FocusScopeMarker InheritedElement can deactivate before
the descendant GestureDetector clears its dependency, tripping the
debug assertion.

This was the same root cause as the keyboard lag: an outer
GestureDetector in the gesture arena ahead of the TextField's own
recognizer ALWAYS interferes, either via the opaque-vs-translucent
arena race (causing lag) or via the InheritedWidget subscription
race (causing the assert).

Fix: remove the outer GestureDetector entirely. Use the built-in
TextField.onTapOutside callback added in Flutter 3.10+ instead:

  TextField(
    ...
    onTapOutside: _onTapOutside,
  )

  void _onTapOutside(PointerDownEvent _) {
    FocusManager.instance.primaryFocus?.unfocus();
  }

Why this is strictly better:

  * FocusManager.instance is a global singleton with NO
    BuildContext dependency. No InheritedWidget is subscribed; no
    _dependents map grows; no race possible on dispose.

  * TextField.onTapOutside uses the framework's TapRegion machinery
    internally. Taps inside the field's TapRegion don't fire the
    callback; only true outside-region taps do. Zero gesture-arena
    interference with the TextField's own tap recognizer \u2014
    keyboard appears synchronously on the first tap with no lag.

  * iOS's default _EditableTextTapOutsideAction at
    flutter/widgets/editable_text.dart:6748-6755 is intentionally a
    no-op for touch on mobile (Apple convention: dismiss via Done
    or swipe-down). Our explicit onTapOutside override is the
    correct way to opt into tap-outside-to-dismiss on mobile
    without fighting iOS conventions or the framework's gesture
    arena.

Wired onTapOutside on all three connect form fields (host /
nickname / password).

flutter build ios --release --no-codesign: 20.5 s, Runner.app
30.2 MB (unchanged). flutter analyze: 6 pre-existing Radio
deprecation infos (unchanged).
2026-05-16 19:18:54 +08:00
..