Skip to content

feat!: synchronous per-detent container layout#735

Merged
lodev09 merged 12 commits into
v4from
feat/synchronous-layout
Jul 15, 2026
Merged

feat!: synchronous per-detent container layout#735
lodev09 merged 12 commits into
v4from
feat/synchronous-layout

Conversation

@lodev09

@lodev09 lodev09 commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

The sheet container is now sized to the sheet's visible height per detent — previously the screen height / largest detent — driven by synchronous Fabric state updates so Yoga layout lands in the same frame as the sheet on both platforms. Requires React Native 0.82+.

  • Core: Per-detent container sizing via Fabric state (containerWidth/containerHeight override Yoga dimensions in the shared shadow node). Flex layouts (flex: 1, justifyContent, etc.) now track the sheet size and relayout on resize.
  • iOS: State commits use unstable_Immediate, so the container relayouts within the same UIKit layout pass and animates with detent transitions. AutoLayout constraints are gone — container and footer are laid out purely by Yoga (footer keyboard avoidance uses a transform).
  • Android: The container resizes per detent and tracks the sheet's visible height in realtime while dragging (clamped to the smallest detent). Kotlin's StateWrapper.updateState is hardcoded async, so a small JNI bridge (TrueSheetStateUpdater) commits with unstable_Immediate for same-frame mount — replacing the scroll-range padding workaround. Falls back to the async path when the native lib isn't available.
  • Scrollables: Auto-detected — ScrollView/FlatList work plugged in directly; insets, keyboard handling, and nested scrolling are wired automatically. The scrollable prop is deprecated and a no-op on native.
  • Web: Non-scrollable sheets get a definite-height flex layout per detent, matching native (auto detents and form sheets keep content-fit sizing).

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Test Plan

Example app on both platforms:

  • FlatListSheet — container/scrollview resizes with the finger during drags, no flicker at settle; floating footer stays pinned through drags and detent transitions
  • Detent resizes (resize(), grabber tap) — content relayouts with the transition, shrinks deferred to settle on Android
  • Keyboard show/hide — container grows before the sheet expands, restores to the pre-keyboard detent
  • auto detents keep content-derived sizing

Checklist

  • I tested on iOS
  • I tested on Android
  • I tested on Web
  • I updated the documentation (if needed)
  • I added a changelog entry (if needed)

🤖 Generated with Claude Code

lodev09 added 5 commits July 15, 2026 00:47
Size the container to the sheet's visible height per detent instead of
screen height / largest detent. Requires RN 0.82+.

- iOS: report all size changes; immediate state updates relayout Yoga
  within the same UIKit layout pass
- Android: per-detent state sizing (grow before animation, shrink on
  settle); remove scroll expansion padding workaround
- JS: container always fills the sheet so flex layouts track detents
- Web: sized flex wrapper for non-scrollable sheets (auto/form keep
  content-fit flow)
Container and footer frames now come purely from Yoga (synchronous
state updates land them in the same UIKit layout pass). Footer is
pinned via bottom:0 style; keyboard avoidance uses a transform.
Removes LayoutUtil.
ScrollViews/FlatLists now plug in directly like a regular view:

- Content fills the sheet by default (except with auto detents, which
  derive height from natural content)
- Native scroll wiring (insets, keyboard, nested scrolling, edge
  effects) always applies to the detected ScrollView
- iOS: removed scrollview frame-forcing; Yoga owns the frame
- scrollable prop is deprecated and a no-op on native
Track the sheet's visible height per-frame during drag (and post-release
settle) so the container resizes with the finger like iOS, killing the
settle flicker. Skip detent reconfigure mid-drag — the size change is
driven by the drag itself and resetting behavior state kills the gesture.
Kotlin's StateWrapper.updateState is hardcoded async, so the container's
Yoga layout lagged the sheet by a frame or two during drags (footer
jumping). Bridge to ConcreteState::updateState with unstable_Immediate —
commits and mounts within the same UI-thread frame, like iOS. Falls back
to the async path when the codegen lib isn't loadable (merged-so builds).
@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-native-true-sheet Ready Ready Preview, Comment Jul 15, 2026 9:47pm

Request Review

Scrollables are auto-detected — the prop was already a no-op on native.
Web now always uses the sized content-fill layout for plugged scrollables.
Docs updated for auto-detection.
@lodev09
lodev09 merged commit 4b9b0e8 into v4 Jul 15, 2026
2 checks passed
@lodev09
lodev09 deleted the feat/synchronous-layout branch July 15, 2026 22:29
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.

1 participant