Problem
On Android, a scrollable sheet whose content is shorter than the sheet but taller than the visible area cannot be scrolled by touch while the keyboard is open. Programmatic scrolling (scrollToOffset) works and reaches the correct offset, so the scroll range genuinely exists — the touch gesture just never reaches the list. The sheet claims it instead, and since the sheet is already at its detent, nothing moves at all.
Cause
The two halves are individually reasonable and interact badly.
TrueSheetContentView.updateScrollViewInsetForKeyboard makes room for the keyboard by adding bottom padding to the pinned scroll view:
val totalBottomInset = if (keyboardHeight > 0) keyboardHeight else bottomInset
setScrollViewPaddingBottom(originalScrollViewPaddingBottom + totalBottomInset)
TrueSheetCoordinatorLayout.onInterceptTouchEvent then decides whether the sheet should take the drag:
val cannotScroll = scrollView != null &&
!hasRefreshControl &&
scrollView.scrollY == 0 &&
!scrollView.canScrollVertically(1)
View.canScrollVertically(1) compares computeVerticalScrollRange() (the child's bottom) against computeVerticalScrollExtent() (the full view height) and ignores the scroll view's own bottom padding. But that padding is exactly how the keyboard inset was applied. So whenever
scrollView.height - paddingBottom < child.height <= scrollView.height
the list has real scroll range while canScrollVertically(1) reports false. cannotScroll becomes true, the coordinator sets dragging = true and calls requestDisallowInterceptTouchEvent(true), and the list never sees the gesture. The bug window is as wide as the keyboard.
Measurements
Instrumented from JS on a sheet with detents={[0.8]}, a FlatList, and a footer (all values dp):
keyboard 336 | list height 773 | content height 675 | scrollTo(99999) settles at y = 226
touchMove events 8 | touchCancel 7 | onScrollBeginDrag 0
226 = 675 − (773 − 324) — the range is real and scrollTo reaches it exactly. Meanwhile onScrollBeginDrag never fires and the touches are cancelled out from under React Native, because the coordinator claimed the stream. content 675 <= list 773, so canScrollVertically(1) is false.
Consistent with this, adding a single comment (pushing content past 773) makes touch scrolling work immediately, and it breaks again on the next present.
Repro
- Android, sheet with
scrollable, a scrolling child, and a footer containing a TextInput.
- Give it enough content to overflow the visible area above the keyboard, but less than the sheet's full height.
- Present the sheet and focus the input so the keyboard opens.
- Swipe the list — nothing moves.
scrollToOffset from a button still scrolls correctly.
Suggested fix
Measure against the padded viewport instead of the full height:
/**
* Whether the scrollable still has room to scroll down.
*
* `View.canScrollVertically` compares the content against the full view height
* and ignores the scroll view's own bottom padding — but that padding is how
* the keyboard inset is applied, so a list with real scroll range reports that
* it cannot scroll.
*/
private fun canScrollDown(scrollView: ViewGroup): Boolean {
val child = scrollView.getChildAt(0) ?: return false
val viewport = scrollView.height - scrollView.paddingTop - scrollView.paddingBottom
return scrollView.scrollY < child.height - viewport
}
and use !canScrollDown(scrollView) in cannotScroll. getChildAt(0) is safe for both types findScrollView matches (ScrollView, NestedScrollView).
Running this as a patch-package patch locally; happy to open a PR if the approach looks right.
Environment
@lodev09/react-native-true-sheet 3.11.10; cannotScroll and the padding-based inset are both unchanged on main (3.11.12)
- React Native 0.86.2, Expo SDK 57, new architecture enabled, edge-to-edge enabled
- Android emulator, Pixel,
react-native-keyboard-controller present
Problem
On Android, a
scrollablesheet whose content is shorter than the sheet but taller than the visible area cannot be scrolled by touch while the keyboard is open. Programmatic scrolling (scrollToOffset) works and reaches the correct offset, so the scroll range genuinely exists — the touch gesture just never reaches the list. The sheet claims it instead, and since the sheet is already at its detent, nothing moves at all.Cause
The two halves are individually reasonable and interact badly.
TrueSheetContentView.updateScrollViewInsetForKeyboardmakes room for the keyboard by adding bottom padding to the pinned scroll view:TrueSheetCoordinatorLayout.onInterceptTouchEventthen decides whether the sheet should take the drag:View.canScrollVertically(1)comparescomputeVerticalScrollRange()(the child's bottom) againstcomputeVerticalScrollExtent()(the full view height) and ignores the scroll view's own bottom padding. But that padding is exactly how the keyboard inset was applied. So wheneverthe list has real scroll range while
canScrollVertically(1)reportsfalse.cannotScrollbecomes true, the coordinator setsdragging = trueand callsrequestDisallowInterceptTouchEvent(true), and the list never sees the gesture. The bug window is as wide as the keyboard.Measurements
Instrumented from JS on a sheet with
detents={[0.8]}, aFlatList, and afooter(all values dp):226 = 675 − (773 − 324)— the range is real andscrollToreaches it exactly. MeanwhileonScrollBeginDragnever fires and the touches are cancelled out from under React Native, because the coordinator claimed the stream.content 675 <= list 773, socanScrollVertically(1)isfalse.Consistent with this, adding a single comment (pushing content past 773) makes touch scrolling work immediately, and it breaks again on the next present.
Repro
scrollable, a scrolling child, and afootercontaining aTextInput.scrollToOffsetfrom a button still scrolls correctly.Suggested fix
Measure against the padded viewport instead of the full height:
and use
!canScrollDown(scrollView)incannotScroll.getChildAt(0)is safe for both typesfindScrollViewmatches (ScrollView,NestedScrollView).Running this as a
patch-packagepatch locally; happy to open a PR if the approach looks right.Environment
@lodev09/react-native-true-sheet3.11.10;cannotScrolland the padding-based inset are both unchanged onmain(3.11.12)react-native-keyboard-controllerpresent