Skip to content

feat(Android, Stack v4): add disable*InsetApplication props#4220

Merged
sgaczol merged 9 commits into
mainfrom
@sgaczol/native-header-bug
Jun 29, 2026
Merged

feat(Android, Stack v4): add disable*InsetApplication props#4220
sgaczol merged 9 commits into
mainfrom
@sgaczol/native-header-bug

Conversation

@sgaczol

@sgaczol sgaczol commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR adds per-edge control over header inset application on Android. Previously only the
top inset could be opted out, via the already-existing disableTopInsetApplication prop.
This PR introduces three new sibling props - disableLeftInsetApplication,
disableRightInsetApplication and disableBottomInsetApplication — so that an app can disable
inset handling on any edge of the native header.

The top inset is a single shared space at the top of the screen, so in nested stacks it must
be coordinated - only the topmost visible header consumes it (to avoid doubled spacing). The top
inset is additionally anchored to the decor view in applyDecorViewTopInsetIfNeeded(), so it
survives a custom SafeAreaView consuming the incoming WindowInsets somewhere in the subtree.
Left/right/bottom are per-edge: each header applies them on its own edges, so there is nothing
to coordinate, and — unlike the top — they have no decor-view fallback. They are read solely from
the incoming WindowInsets and applied per-header, which is why a SafeAreaView in the subtree
can consume them per-subtree. The context only propagates the opt-out down the subtree (set the
prop once on the outer stack, inner headers inherit it).

Closes https://github.com/software-mansion/react-native-screens-labs/issues/1535

Changes

  • Added public props disableLeftInsetApplication, disableRightInsetApplication and
    disableBottomInsetApplication to ScreenStackHeaderConfigProps (src/types.tsx)
  • Added consumeLeftInset / consumeRightInset / consumeBottomInset to the native component
    spec (ScreenStackHeaderConfigNativeComponent.ts) and the corresponding setters on the Android
    view manager / config (ScreenStackHeaderConfigViewManager.kt, ScreenStackHeaderConfig.kt)
  • Introduced EdgeInsetApplicationContext carrying the left/right/bottom opt-out down the subtree, and wired the providers in ScreenStackItem.tsx and the consumer in ScreenStackHeaderConfig.tsx
  • Reworked the inset computation in CustomToolbar.kt: each edge (top/left/right/bottom) is now
    evaluated independently
  • Added an issue test

After - visual documentation

top.inset.mov
left.inset.mov
right.inset.mov
bottom.inset.mov

Test plan

Run the 4220 issue test on different devices and check if insets are disabled correctly.

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-edge opt-out controls for applying Android header insets in the Stack v4 implementation, extending the existing top-edge opt-out to left/right/bottom and wiring the decisions through JS context down to native toolbar padding.

Changes:

  • Introduces new public props: disableLeftInsetApplication, disableRightInsetApplication, disableBottomInsetApplication.
  • Adds new native props (consumeLeftInset, consumeRightInset, consumeBottomInset) and Android-side plumbing to apply them.
  • Refactors Android header inset computation to evaluate each edge independently; adds an issue-test app screen (Test4220) to exercise the behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/types.tsx Adds the new public disable*InsetApplication props to the JS API types.
src/fabric/ScreenStackHeaderConfigNativeComponent.ts Extends the Fabric native component spec with consumeLeft/Right/BottomInset.
src/components/ScreenStackItem.tsx Adds edge-inset context providers to propagate opt-out through nested stacks.
src/components/ScreenStackHeaderConfig.tsx Consumes the new edge-inset context and forwards consume*Inset to native.
src/components/contexts/EdgeInsetApplicationContext.ts Introduces contexts + hook for propagating left/right/bottom opt-out down the subtree.
android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt Adds setters for the new consume*Inset props.
android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt Stores the new consume*Inset flags and requests inset re-application on change.
android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt Reworks inset calculation so top/left/right/bottom are handled independently.
apps/src/tests/issue-tests/Test4220.tsx Adds a manual issue test UI to toggle the new props and validate behavior.
apps/src/tests/issue-tests/index.ts Exports the new Test4220 entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/types.tsx
Comment thread src/types.tsx
Comment thread src/types.tsx
Comment thread src/components/contexts/EdgeInsetApplicationContext.ts
Comment thread src/types.tsx Outdated
* When set to `true`, disables left inset handling for this header and all headers in its
* subtree.
*
* Once disabled by an ancestor, it cannot be re-enabled by descendants.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once inset application is disabled == once flag is enabled is a bit confusing, maybe we should be more precise here. Applies to other jsdocs as well.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay: 9ee5e8d

Comment thread src/components/ScreenStackItem.tsx Outdated
Comment on lines +146 to +150
<TopInsetApplicationContext.Provider value={nextContextValue}>
<DebugContainer
contentStyle={contentStyle}
style={debugContainerStyle}
stackPresentation={stackPresentationWithDefault}>
{shouldUseSafeAreaView ? (
<SafeAreaView edges={getSafeAreaEdges(headerConfig)}>
{children}
</SafeAreaView>
) : (
children
)}
</DebugContainer>
<LeftInsetApplicationContext.Provider value={nextLeftContextValue}>
<RightInsetApplicationContext.Provider value={nextRightContextValue}>
<BottomInsetApplicationContext.Provider
value={nextBottomContextValue}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should use just one context with multiple props (or two - one for top, one for rest)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, there's a single context now: 42c4653 & b488fd7

} else {
0
}
val bottomInset = if (shouldHandleBottomInset) cutoutInsets.bottom else 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, should the bottom inset be ever applied? But that's out of scope of this PR.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I don't know if there's any reasonable use case for that. Also it is probably applied very rarely and looks pretty weird (look at the video regarding bottom inset). It's certainly a thing to discuss.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when the phone has a double cutout, the padding to the header will be applied and it won't look good; I think we should create a ticket to zero out the bottom padding

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@t0maboro t0maboro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runtime seems to be working fine, left some cleanup requests

Comment thread apps/src/tests/issue-tests/Test4220.tsx
Comment thread src/components/ScreenStackItem.tsx Outdated
Comment thread android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt Outdated
Comment thread android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment on lines +7 to +13
* The semantics differ between the top edge and the rest:
* - `topAlreadyApplied`: the top inset is coordinated "consume-once" across nested headers,
* so `true` means an ancestor header already applied it and the subtree should skip it.
* - `left/right/bottomDisabled`: these insets are applied per-header (every header spans the
* full width and needs the inset on its own edges, and a custom `SafeAreaView` may consume
* them for a part of the subtree). The flags therefore only propagate the opt-out down the
* subtree: `true` means an ancestor header opted out, so the whole subtree should skip it.
@sgaczol sgaczol merged commit 1608811 into main Jun 29, 2026
7 of 8 checks passed
@sgaczol sgaczol deleted the @sgaczol/native-header-bug branch June 29, 2026 13:43
sgaczol added a commit that referenced this pull request Jun 30, 2026
## Description

This PR adds per-edge control over header inset application on Android.
Previously only the
top inset could be opted out, via the already-existing
`disableTopInsetApplication` prop.
This PR introduces three new sibling props -
`disableLeftInsetApplication`,
`disableRightInsetApplication` and `disableBottomInsetApplication` — so
that an app can disable
inset handling on any edge of the native header.

The top inset is a single shared space at the top of the screen, so in
nested stacks it must
be coordinated - only the topmost visible header consumes it (to avoid
doubled spacing). The top
inset is additionally anchored to the decor view in
`applyDecorViewTopInsetIfNeeded()`, so it
survives a custom `SafeAreaView` consuming the incoming `WindowInsets`
somewhere in the subtree.
Left/right/bottom are per-edge: each header applies them on its own
edges, so there is nothing
to coordinate, and — unlike the top — they have no decor-view fallback.
They are read solely from
the incoming `WindowInsets` and applied per-header, which is why a
`SafeAreaView` in the subtree
can consume them per-subtree. The context only propagates the opt-out
down the subtree (set the
prop once on the outer stack, inner headers inherit it).

Closes
software-mansion/react-native-screens-labs#1535

## Changes

- Added public props `disableLeftInsetApplication`,
`disableRightInsetApplication` and
`disableBottomInsetApplication` to `ScreenStackHeaderConfigProps`
(`src/types.tsx`)
- Added `consumeLeftInset` / `consumeRightInset` / `consumeBottomInset`
to the native component
spec (`ScreenStackHeaderConfigNativeComponent.ts`) and the corresponding
setters on the Android
view manager / config (`ScreenStackHeaderConfigViewManager.kt`,
`ScreenStackHeaderConfig.kt`)
- Introduced `EdgeInsetApplicationContext` carrying the
left/right/bottom opt-out down the subtree, and wired the providers in
`ScreenStackItem.tsx` and the consumer in `ScreenStackHeaderConfig.tsx`
- Reworked the inset computation in `CustomToolbar.kt`: each edge
(top/left/right/bottom) is now
  evaluated independently
- Added an issue test


## After - visual documentation



https://github.com/user-attachments/assets/1f747d3b-00d2-46f1-9058-b0bce3f02d61


https://github.com/user-attachments/assets/279e1b58-429d-4183-947a-ffa68e4129bb


https://github.com/user-attachments/assets/5fee3e50-d519-4fc1-b694-9b0465ea28a0


https://github.com/user-attachments/assets/26b33fea-5486-4e82-9f8b-01c39ee8c9e9



## Test plan

Run the 4220 issue test on different devices and check if insets are
disabled correctly.

## Checklist

- [x] Included code example that can be used to test this change.
- [x] For visual changes, included screenshots / GIFs / recordings
documenting the change.
- [ ] For API changes, updated relevant public types.
- [ ] Ensured that CI passes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants