Skip to content

Commit cf5dc93

Browse files
authored
fix(Android, Stack): Fix FormSheet - SafeAreaView integration (#3336)
## Description Bugfixing the integration between SafeAreaView and FormSheet. I'm preventing the consumption of the bottom inset, allowing it to be passed to SAV component. Additionally, I'm adding some other fixes I caught during testing, for fitToContents and zeroing the bottom inset for keyboard handling. > [!WARNING] > Note: > We do not manipulate the top inset manually. Therefore, if `SafeAreaView` has top insets enabled, we must retain the top inset even if the formSheet does not currently overflow into the status bar. > > This is important because in some specific edge cases - for example, when the keyboard slides in - the formSheet might overlap the status bar. If we ignored the top inset and it suddenly became necessary, it would result in a noticeable visual content jump. To ensure consistency and avoid layout shifts, we always include the top inset upfront, which can be disabled from the application perspective. Creating a follow-up ticket for handling some cases that I wasn't able to cover with SAV: software-mansion/react-native-screens-labs#565 Fixes: software-mansion/react-native-screens-labs#457 , #2896 ## Changes - Updated the logic for consuming insets in the `SheetDelegate` - Added a new example for testing. ## Screenshots / GIFs Here you can add screenshots / GIFs documenting your change. You can add before / after section if you're changing some behavior. ### Before https://github.com/user-attachments/assets/c32fc9f4-63e3-41b2-ab31-110a3ec46d68 ### After https://github.com/user-attachments/assets/647c1674-535e-4dd8-8156-9b490588d123 ## Test code and steps to reproduce Added `Test3336` for testing. ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
1 parent 82286a2 commit cf5dc93

File tree

4 files changed

+463
-16
lines changed

4 files changed

+463
-16
lines changed

android/src/main/java/com/swmansion/rnscreens/Screen.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ class Screen(
142142
if (usesFormSheetPresentation()) {
143143
if (isSheetFitToContents()) {
144144
sheetBehavior?.useSingleDetent(height)
145+
// During the initial call in `onCreateView`, insets are not yet available,
146+
// so we need to request an additional layout pass later to account for them.
147+
requestLayout()
145148
}
146149

147150
if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {

android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetDelegate.kt

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -311,26 +311,14 @@ class SheetDelegate(
311311
): WindowInsetsCompat {
312312
val isImeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
313313
val imeInset = insets.getInsets(WindowInsetsCompat.Type.ime())
314+
val prevSystemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
314315

315316
if (isImeVisible) {
316317
isKeyboardVisible = true
317318
keyboardState = KeyboardVisible(imeInset.bottom)
318319
sheetBehavior?.let {
319320
this.configureBottomSheetBehaviour(it, keyboardState)
320321
}
321-
322-
val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
323-
return WindowInsetsCompat
324-
.Builder(insets)
325-
.setInsets(
326-
WindowInsetsCompat.Type.navigationBars(),
327-
Insets.of(
328-
prevInsets.left,
329-
prevInsets.top,
330-
prevInsets.right,
331-
0,
332-
),
333-
).build()
334322
} else {
335323
sheetBehavior?.let {
336324
if (isKeyboardVisible) {
@@ -344,12 +332,19 @@ class SheetDelegate(
344332
isKeyboardVisible = false
345333
}
346334

347-
val prevInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
335+
val newBottomInset = if (!isImeVisible) prevSystemBarsInsets.bottom else 0
336+
337+
// Note: We do not manipulate the top inset manually. Therefore, if SafeAreaView has top insets enabled,
338+
// we must retain the top inset even if the formSheet does not currently overflow into the status bar.
339+
// This is important because in some specific edge cases - for example, when the keyboard slides in -
340+
// the formSheet might overlap the status bar. If we ignored the top inset and it suddenly became necessary,
341+
// it would result in a noticeable visual content jump. To ensure consistency and avoid layout shifts,
342+
// we always include the top inset upfront, which can be disabled from the application perspective.
348343
return WindowInsetsCompat
349344
.Builder(insets)
350345
.setInsets(
351-
WindowInsetsCompat.Type.navigationBars(),
352-
Insets.of(prevInsets.left, prevInsets.top, prevInsets.right, 0),
346+
WindowInsetsCompat.Type.systemBars(),
347+
Insets.of(prevSystemBarsInsets.left, prevSystemBarsInsets.top, prevSystemBarsInsets.right, newBottomInset),
353348
).build()
354349
}
355350

0 commit comments

Comments
 (0)