fix(Android): don't block window insets animation dispatch from ScreenFooter#4149
Open
stachbial wants to merge 1 commit into
Open
Conversation
…nFooter ScreenFooter installed a WindowInsetsAnimationCompat.Callback with DISPATCH_MODE_STOP on the activity decor view and never removed it. Since DISPATCH_MODE_STOP halts dispatch to the whole subtree - and the decor view is the root of the window - mounting a single formSheet footer permanently disabled keyboard insets animations for every other consumer in the app (reanimated useAnimatedKeyboard, react-native-keyboard-controller, ...). Switch the callback to DISPATCH_MODE_CONTINUE_ON_SUBTREE (it never consumes insets, so propagation is safe) and tie registration to the view's window attachment so the decor view callback slot is restored on detach.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
On Android,
ScreenFooterinstalls aWindowInsetsAnimationCompat.CallbackwithDISPATCH_MODE_STOPon the activity's decor view (in itsinitblock) and never removes it. Android keeps a single insets-animation callback slot per view, andDISPATCH_MODE_STOPstops dispatch to the entire subtree of the view it is set on — and the decor view is the root of the window. As a result, once a single formSheet withunstable_sheetFootermounts, no other view in the window ever receivesonStart/onProgress/onEndagain, and the callback stays installed even after the footer unmounts.With edge-to-edge enabled (the default since React Native 0.81, enforced on Android 15+),
WindowInsetsAnimationdispatch is the only keyboard signal libraries like reanimated (useAnimatedKeyboard) andreact-native-keyboard-controllerrely on. So mounting one sheet footer permanently breaks keyboard handling/animations in the whole app until the process is restarted.The interference was already flagged as a known issue in the code itself:
Minimal reproduction
React Native 0.83 / react-native-screens 4.x, Android only (reproduced on API 33 and API 36). Included in this PR as
apps/src/tests/issue-tests/TestScreenFooterKeyboardInsets.tsx— aHomescreen with aTextInputtranslated byuseAnimatedKeyboard, plus a formSheet route withunstable_sheetFooter.Steps (against unpatched
main):TextInput— the input animates up with the keyboard (insets animation dispatch works).TextInputagain — the keyboard animation no longer runs (useAnimatedKeyboardreports nothing), and it never recovers for the lifetime of the process.Changes
insetsAnimationcallback's dispatch mode fromDISPATCH_MODE_STOPtoDISPATCH_MODE_CONTINUE_ON_SUBTREE. The callback never consumes insets inonProgress, so continuing dispatch is safe and other consumers in the window keep receiving insets animations.initblock and tied it to window attachment: it is registered inonAttachedToWindowand unregistered (slot restored tonull) inonDetachedFromWindow. AWeakReference<ScreenFooter>in the companion object tracks which footer currently occupies the decor view's single callback slot, so one footer's detach cannot clear a callback registered by a more recently attached footer. The decor view is now resolved null-safely viareactContext.currentActivity?.window?.decorView.Why keep the callback on the decor view at all
The footer cannot listen on itself or its
Screenparent: every formSheet'sScreenview carries its ownDISPATCH_MODE_STOPcallback (ScreenStackFragment), so nothing inside the sheet's subtree receives insets-animation dispatch. Listening on the decor view is the pragmatic way for the footer to keep tracking the keyboard — this PR keeps that approach and only removes its side effects on the rest of the window. A deeper alternative would be to drive the footer fromSheetDelegate's existing keyboard-insets handling (it already observes IME progress for the sheet) and drop the decor-view callback entirely — happy to explore that instead if you prefer, but it felt too invasive for a first contribution.Test plan
apps/src/tests/issue-tests/TestScreenFooterKeyboardInsets.tsx(added in this PR): after opening and closing the form sheet (footer mount + unmount), focusing theTextInputon the Home screen still animates with the keyboard. On unpatchedmain, step 3 of the reproduction fails.apps/src/tests/issue-tests/TestFormSheet.tsxwith the commented-outunstable_sheetFooter: FormSheetFooteroption re-enabled: the footer still tracks the sheet and stays positioned above the keyboard when aTextInputinside the sheet is focused (the footer's own insets-animation handling is unchanged — it still receivesonStart/onProgress/onEndwhile attached).Checklist