Repository navigation
feat: respect window insets a parent adjusts for its subtree on Android - #791
Merged
Merged
Conversation
SafeAreaProvider and SafeAreaView now compute insets from the WindowInsets dispatched to the provider view, so a parent that adjusts insets for its subtree is respected. Root window insets remain the fallback when no dispatch reached the view. The example app gains a native test container that adds bottom insets for its children.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
React Native's translucent StatusBar lowers the dispatched top inset to 0 to skip fitsSystemWindows padding, which made the safe area top 0. Dispatched insets now only add to the root insets.
The Additional Insets example renders its content in a react-native-screens Screen with additionalSafeAreaInsets, applied to the example through a temporary patch-package patch from software-mansion/react-native-screens#4998. It replaces the custom native test container.
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.
Summary
On Android,
SafeAreaProvider(and souseSafeAreaInsetsandSafeAreaView) now computes insets from theWindowInsetsdispatched to the provider instead of the root window insets. A parent that adjusts insets for its subtree, by overridingdispatchApplyWindowInsetsor with anOnApplyWindowInsetsListenerthat returns larger insets, is now respected. iOS already behaves this way, since the provider reads its ownsafeAreaInsets.Motivation: software-mansion/react-native-screens#4998 adds an
additionalSafeAreaInsetsprop toScreen, mirroring UIKit, so a custom floating tab bar drawn in JS over the content can add its height to the bottom insets of the screen's subtree. On iOS it sets the screen controller'sadditionalSafeAreaInsets, which safe-area-context already picks up through UIKit with no library change. On Android the screen enlarges theWindowInsetsit dispatches to its children, and safe-area-context needs this PR to see them.The inset math is unchanged: the same inset types on API 30+, the same keyboard exclusion on API 23-29, and the same per-view clipping, so a provider that does not reach an edge still gets 0 there.
Dispatched insets can only add to the root insets. Each edge is the max of the root and dispatched value. Ancestors also lower the insets they dispatch without making that area safe: React Native's translucent
StatusBar(setStatusBarTranslucency) dispatches a top inset of 0 sofitsSystemWindowsadds no padding, and react-native-screens'ScreenWindowTraitsdoes the same. Taking dispatched values as-is made the top inset 0 under a translucent status bar.Provider inside the container is required.
SafeAreaViewstill reads its nearest provider's insets, as on iOS, so a container between the provider and theSafeAreaViewis only picked up when aSafeAreaProvidersits inside it. For react-native-screens, that means a provider inside each screen.Fallback to root insets. When no dispatch has reached the provider, the root insets are used as before. That covers non edge-to-edge apps, where the decor view consumes the insets and nothing is dispatched to React Native views, and providers under an ancestor that consumes insets (React Native's own
SafeAreaViewreturnsCONSUMED). If the root insets change and no new dispatch reached the provider, the stored value is treated as stale and the root insets are used. API < 23 keeps thegetWindowVisibleDisplayFramepath.requestApplyInsets()on attach. Views added after the last dispatch only receive insets on the next one, so the provider requests a dispatch when it attaches. React Native core'sReactSafeAreaViewdoes the same. ASafeAreaViewwithout a provider records and requests its own dispatched insets.The example app gets an "Additional Insets" screen: content in a react-native-screens
Screen(throughScreenStack/ScreenStackItem) withadditionalSafeAreaInsets={{ bottom: 80 }}and a toggle, holding aSafeAreaProvider, aSafeAreaViewand aScrollView, next to the root provider's values. software-mansion/react-native-screens#4998 is not released, so the example applies it to react-native-screens 4.25.2 with patch-package (example/patches/react-native-screens+4.25.2+001+additional-safe-area-insets-from-pr-4998.patch, from software-mansion/react-native-screens#4998 commitscc3654aand6b0d209). The patch is temporary and goes away once software-mansion/react-native-screens#4998 ships.Risks
getSafeAreaInsets.min(systemWindowInsetBottom, stableInsetBottom)to exclude the keyboard, so extra bottom insets added throughreplaceSystemWindowInsetsare capped at the stable inset.Related: #603 (edge-to-edge), #738 and #758 (transient insets), #555, #634 and #667 (insets reported on Samsung devices). This PR does not claim to fix any of them.
Test Plan
Example app, new architecture. Open the dev menu and choose "Show Additional Insets Example". The switch toggles
additionalSafeAreaInsetsbottom 80 on the screen.114(34 + 80),34with the switch off. TheSafeAreaViewpadding and the scroll view's automatic content inset both end at the top of the JS tab bar. iOS native code in this repo is unchanged.104(24 + 80),24with the switch off, updated live. The scroll view usesuseSafeAreaInsetsfor its bottom padding on Android, since Android scroll views have no automatic insets.24with the switch on.top 116 · bottom 34on iOS, where react-native-test-app hosts the app in a navigation controller, andtop 24 · bottom 24on Android).Regression checks on Android API 36 (edge-to-edge forced by targetSdk 36), gesture navigation:
0/0/0/0, modal screentop 24 · bottom 24, same as main.24/24with the keyboard open (Gboard), in landscape, and with<StatusBar translucent />(temporary edit, not in the diff; the first commit alone gavetop 0there).Modal(separate dialog window,statusBarTranslucentandnavigationBarTranslucent) with a provider inside:top 24 · bottom 24. This was checked with a temporary screen that is not in the diff.ModalAlso run on an API 30 emulator, which is not edge-to-edge in this example app. Both providers report
0/0, because the decor consumes the insets and nothing is dispatched to the React Native views. The fallback gives the same values as main.Not tested: the old architecture (same Kotlin classes, no arch-specific code), API 23-29 devices, and a
SafeAreaViewrendered without any provider. iOS native code is unchanged.