You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug
When using KeyboardChatScrollView with inverted props, dismissing the keyboard from the Android IME close buton (the down-arrow in the navigation bar) snaps the content instantly instead of animating. A non-inverted list animates correctly.
Repo for reproducing
Using the provided example and selecting "use flashlist" + "inverted"
To Reproduce
Steps to reproduce the behavior:
Go to Chat Kit
Click on open config
Select "Toggle inverted"
Select "use Flash List"
Expected behavior
Clicking the IME close button should animate the keyboard.
Screenshots
WhatsApp.Video.2026-05-27.at.22.40.43.mp4
Notes: Sorry I'm dumb I just realize we don't see the IME button on the video, it's this button:
Smartphone (please complete the following information):
Desktop OS: /
Device: Samsung galaxy A56
OS: Android 36
RN version: Whatever is provided in the example (but on my app it's RN 81, expo 54)
RN architecture: new arch
JS engine: Hermes
Library version: 1.21.8
Additional context
Add any other context about the problem here.
Root cause Disclaimer : All the following content is found/suggested by AI (Claude opus 4.7)
The inverted branch of useChatKeyboard skips every event with duration === -1:
The guard was meant to drop the post-interactive snap-back events that follow a
finger swipe-dismiss. But duration === -1 is not unique to snap-back — it's emitted
by any app-controlled inset animation (controlWindowInsetsAnimation(..., -1, ...) in
KeyboardAnimationController.kt). On some Android devices/IMEs (reproduced on a
Samsung SM-A566B, API 36) the IME close button drives the hide through that same
controller, so it produces a full animated onMove sequence carrying duration === -1.
The inverted branch discards the whole sequence → no animation.
Suggested fix
Distinguish a real interactive gesture from an animated close instead of keying off duration alone. A finger
dismiss emits onInteractive events first; the close button does not. Track an interactive flag (set in
onInteractive, reset on a genuine duration !== -1 start) and only skip duration === -1 events when that flag is
set.
For what it worth we're using this patch from claude:
Index: src/components/KeyboardChatScrollView/useChatKeyboard/index.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================diff --git a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ts b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ts--- a/src/components/KeyboardChatScrollView/useChatKeyboard/index.ts (revision adafcc8cda54a4dce84140ae3014bfa7365c208c)+++ b/src/components/KeyboardChatScrollView/useChatKeyboard/index.ts (date 1779916883510)@@ -50,6 +50,11 @@
const closing = useSharedValue(false);
const minimumPaddingFractionOnOpen = useSharedValue(0);
const actualOpenShift = useSharedValue(0);
+ // Tracks whether a finger-driven interactive gesture just happened. Used to+ // distinguish a genuine post-interactive snap-back (which must be skipped) from+ // an animated close that Android reports with `duration === -1` but no preceding+ // interactive events (e.g. the IME close button on some devices).+ const recentlyInteractive = useSharedValue(false);
const {
layout,
size,
@@ -86,6 +91,12 @@
return;
}
+ // A genuine (system-driven) start resets the interactive flag. Snap-back+ // and close-button starts arrive with `duration === -1` and must not reset it.+ if (e.duration !== -1) {+ recentlyInteractive.value = false;+ }+
if (e.height > 0) {
// eslint-disable-next-line react-compiler/react-compiler
targetKeyboardHeight.value = e.height;
@@ -128,10 +139,12 @@
minimumPaddingAbsorbed,
);
- if (inverted && e.duration === -1) {+ if (inverted && e.duration === -1 && recentlyInteractive.value) {
// Android inverted: skip post-interactive snap-back events
- // (duration === -1 means the keyboard is re-establishing its- // position after an interactive gesture, not a real animation)+ // (duration === -1 after an interactive gesture means the keyboard is+ // re-establishing its position, not a real animation). Only skip when a+ // finger gesture actually preceded it — an animated close (e.g. IME close+ // button) also uses duration === -1 but must animate.
return;
} else if (e.height > 0) {
// Android: keyboard opening — set padding + capture scroll position
@@ -173,8 +186,9 @@
currentHeight.value = e.height;
if (inverted) {
- // Skip post-interactive snap-back (duration === -1)- if (e.duration === -1) {+ // Skip post-interactive snap-back (duration === -1), but only when a+ // finger gesture preceded it — an animated close also uses duration === -1.+ if (e.duration === -1 && recentlyInteractive.value) {
return;
}
@@ -357,6 +371,13 @@
actualOpenShift.value = scroll.value - offsetBeforeScroll.value;
}
},
+ onInteractive: () => {+ "worklet";++ // A finger-driven gesture happened; the following `duration === -1`+ // snap-back events (if any) should be skipped by onStart/onMove.+ recentlyInteractive.value = true;+ },
},
[inverted, keyboardLiftBehavior, offset],
);
Describe the bug
When using KeyboardChatScrollView with inverted props, dismissing the keyboard from the Android IME close buton (the down-arrow in the navigation bar) snaps the content instantly instead of animating. A non-inverted list animates correctly.
Repo for reproducing
Using the provided example and selecting "use flashlist" + "inverted"
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Clicking the IME close button should animate the keyboard.
Screenshots
WhatsApp.Video.2026-05-27.at.22.40.43.mp4
Notes: Sorry I'm dumb I just realize we don't see the IME button on the video, it's this button:
Smartphone (please complete the following information):
Additional context
Add any other context about the problem here.
Root cause
Disclaimer : All the following content is found/suggested by AI (Claude opus 4.7)
For what it worth we're using this patch from claude:
With this patch, no more problems:
WhatsApp.Video.2026-05-27.at.23.24.56.mp4