Fix several race conditions in the iOS interactive dismiss flow:
- Don't skip keyboard close notifications during pan tracking, which
left Dart unaware the keyboard was dismissed
- Guard resignFirstResponder with generation check so reopened keyboards
aren't killed by stale dismiss completions
- Block pan tracking from starting during an in-flight dismiss animation
- Always reset keyboard view bounds when the keyboard opens, not just
when the dismiss animator is still running
- Handle duration=0 keyboard notifications by snapping immediately
- Gate adaptive learning debug output behind kDebugMode
Rewrite the example as a chat UI demonstrating ListenableBuilder,
scroll freeze during interactive dismiss, and proper bottom inset
handling with max(keyboardHeight, safeBottom).
Modernize example Android project from v1 to v2 embedding with
current AGP/Gradle versions.
- Dart-side animation replay using exact native curve (21-point lookup
table sampled from CADisplayLink) with 16ms start offset and 10ms
shorter duration to stay ahead of the native animation pipeline.
- Snap-back reads bounds presentation layer so height doesn't jump.
- Dismiss defers resignFirstResponder until bounds animation completes,
with immediate Dart close animation via animTarget/animGeneration.
- enableInteractiveDismiss accepts trackingInset to widen the gesture
zone above the keyboard (for composer height).
- FFI: anim_target, anim_duration, anim_gen, system_keyboard_height,
is_tracking exposed for Dart animation and scroll physics.