fix(android): keep BottomNavigationView pinned under the keyboard in edge-to-edge#549
Conversation
…edge-to-edge Material's BottomNavigationView adds the bottom system-window inset, which includes the IME inset under edge-to-edge, to its own padding, so the native tab bar rises above the keyboard. Give ExtendedBottomNavigationView its own inset listener applying system bars and display cutout insets only, excluding the IME type, and return the insets unconsumed so tab-screen inputs still receive keyboard avoidance. This is a no-op outside edge-to-edge. Fixes callstack#357
|
We independently root-caused this exact bug in a production React Native app and arrived at the same fix as this PR before finding it — confirming the approach from a second angle. New data worth having for the merge decision: this now reproduces even in apps that were previously shielded. Our app wraps everything in
Same APK everywhere — the trigger is purely the OS build, so this is switching on device-by-device as Android 17 / One UI updates roll out. All input fields, every screen with the native bar. Verification of this PR's fix: we shipped the identical change via patch-package and confirmed on-device — Android 17 emulator, S26 Ultra and Pixel 10 Pro fixed (keyboard overlays the bar again), and no visual change at rest on the previously-working devices (system-bar/cutout padding preserved). Would love to see this merged + released so we can drop the patch. |
What
Under edge-to-edge on Android, focusing a text input pushes the native tab bar up on top of the keyboard instead of letting the keyboard overlay it (iOS behavior). Fixes #357.
Why
The library delegates inset handling to Material.
BottomNavigationViewinstalls a default inset listener in its constructor that addsgetSystemWindowInsetBottom()to its bottom padding, and that value includes the IME inset. WithdecorFitsSystemWindows=falsethe window is not resized, so the IME inset propagates down to the bar and Material pads it by the keyboard height every frame (material-components/material-components-android#493).windowSoftInputMode="adjustPan"does not help whenreact-native-keyboard-controlleris present, since it forcesadjustResizeat runtime.How
Give
ExtendedBottomNavigationViewits ownOnApplyWindowInsetsListenerthat applies system bars + display cutout insets only, excludingType.ime(), and returns the insets unconsumed so sibling views (the tab-screen content that hosts inputs) still receive the IME inset. Gesture-nav clearance is preserved because system-bar and cutout insets are still applied as padding.This is a no-op outside edge-to-edge: with
decorFitsSystemWindows=truethe decor consumes the system insets before they reach the bar, so the computed insets are zero and padding stays at its base, and the keyboard resizes the window rather than dispatching an IME inset. Behavior only changes for the edge-to-edge case that is currently broken.Test plan