Skip to content

Android: list cannot be scrolled by touch while the keyboard is open (cannotScroll ignores the scroll view's bottom padding) #791

Description

@dincozdemir

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

  1. Android, sheet with scrollable, a scrolling child, and a footer containing a TextInput.
  2. Give it enough content to overflow the visible area above the keyboard, but less than the sheet's full height.
  3. Present the sheet and focus the input so the keyboard opens.
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs reproNeed to replicate this issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions