react-native: nestedScrollEnabled prop not working in ScrollView (Android) with horizontal direction

Environment

Binaries: npm: 5.6.0 - C:\Program Files\nodejs\npm.CMD IDEs: Android Studio: Version 3.1.0.0 AI-173.4907809 Package: “react”: “16.5.0”, “react-native”: “0.57.1”, Device Info: Android Version: 5.0.2 LRX22G

Description

I think nestedScrollEnabled prop for ScrollView only work in vertical. I don’t know what’s wrong in my code or something else. Just change little bit in height and width length and add/remove nestedScrollEnabled. Could anyone help me in this issue.

Reproducible Demo

VERTICAL

<ScrollView >
        <View style={{ backgroundColor: 'red', borderRightColor: 'white', borderWidth: 3, width: 500, height: 500 }}>
          <View style={{ borderWidth: 2, borderColor: 'blue', backgroundColor: 'yellow', width: 100, height: 300 }}>
            <ScrollView  nestedScrollEnabled>
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
            </ScrollView>
          </View>
        </View>
        <View style={{ backgroundColor: 'red', borderRightColor: 'white', borderWidth: 3, width: 500, height: 500 }}>
          <View style={{ borderWidth: 2, borderColor: 'blue', backgroundColor: 'yellow', width: 100, height: 300 }}>
            <ScrollView  nestedScrollEnabled>
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
              <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
            </ScrollView>
          </View>
        </View>
      </ScrollView>

vertical-scroll

HORIZONTAL

<ScrollView horizontal>
  <View style={{backgroundColor: 'red',borderRightColor: 'white', borderWidth: 3, width: 500, height: 500}}>
    <View style={{ borderWidth: 2, borderColor: 'blue', backgroundColor: 'yellow', width: 300, height: 100 }}>
      <ScrollView horizontal nestedScrollEnabled>
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
      </ScrollView>
    </View>
  </View>
  <View style={{backgroundColor: 'red',borderRightColor: 'white', borderWidth: 3, width: 500, height: 500}}>
    <View style={{ borderWidth: 2, borderColor: 'blue', backgroundColor: 'yellow', width: 300, height: 100 }}>
      <ScrollView horizontal nestedScrollEnabled>
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
        <View style={{ borderWidth: 3, borderColor: 'black', backgroundColor: 'purple', width: 100, height: 100 }} />
      </ScrollView>
    </View>
  </View>
</ScrollView>

horizontal-scroll

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 15
  • Comments: 67 (12 by maintainers)

Commits related to this issue

Most upvoted comments

I found an easy workaround!

If we use the ScrollView from react-native-gesture-handler everything works as expected.

import { ScrollView } from 'react-native'
import { ScrollView as GestureHandlerScrollView } from 'react-native-gesture-handler'

<ScrollView horizontal>
    <GestureHandlerScrollView horizontal />
</ScrollVIew>

You can have nested horizontal FlatLists like this:

<FlatList {...props} renderScrollComponent={p => <GestureHandlerScrollView {...p} />} />

Updated the snack with this workaround: https://snack.expo.io/@brunolemos/nested-scrollview

gif-android-working </div>

Maybe someone can check what react-native-gesture-handle is doing and bring the same workaround to the core of react-native.

Just bumping in here to show people are still waiting for this fix. Can we get some maintainers to review this PR and get the fix in already.

Confirm, I have the same issue with nested horizontal scrolling. Please reopen, environment info added.

Anyone? I believe it’s a bug.

on all the scrollviews add prop nestedScrollEnabled, including the nested scrollview!

I mean shouldn’t this issue be fixed in react-native core? The solution can’t be just use a ScrollView from a different library.

Oh No. I’m facing this as well. Nested horizontal scrollviews doesn’t work on Android. nestedScrollEnabled has no effect in this case. Still happening on react-native 0.61.

I made this Snack with a reproduction: https://snack.expo.io/@brunolemos/nested-scrollview

<div> gif-ios-works gif-android-doesnt-work </div>
See full code
import React from 'react'
import { ScrollView, ScrollViewProps, View, ViewProps } from 'react-native'

function App() {
  return <Columns />
}

export default App

function Story(props: ViewProps) {
  return (
    <View
      {...props}
      style={[
        {
          width: 30,
          height: 30,
          borderRadius: 30 / 2,
          backgroundColor: 'blue',
        },
        props.style,
      ]}
    />
  )
}

function Stories(props: ScrollViewProps) {
  return (
    <ScrollView
      horizontal
      nestedScrollEnabled
      {...props}
      style={[
        {
          width: '100%',
          maxHeight: 30 + 2 * 2,
          backgroundColor: 'lightyellow',
        },
        props.style,
      ]}
    >
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
      <Story style={{ margin: 2 }} />
    </ScrollView>
  )
}

function Column(props: ViewProps) {
  return (
    <View
      {...props}
      style={[
        {
          paddingTop: 40,
          width: 200,
          height: '100%',
          backgroundColor: 'lightgreen',
        },
        props.style,
      ]}
    >
      <Stories />
    </View>
  )
}

function Columns(props: ScrollViewProps) {
  return (
    <ScrollView
      horizontal
      nestedScrollEnabled
      {...props}
      style={[
        { width: '100%', height: '100%', backgroundColor: 'lightblue' },
        props.style,
      ]}
    >
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
      <Column style={{ margin: 10 }} />
    </ScrollView>
  )
}

Having the same issue. nestedScrollEnabled property works fine for vertical/vertical or vertical/horizontal (and vice versa) but seems to have no effect for nested horizontal scrollviews inside of another horizontal scrollview. However, i found some kind of workaround for this. This is a bit hacky and does not work perfectly, as you have to actually drag the inner view, but it works. You can capture the onTouchStart, onTouchEnd and onTouchCancel Events of the inner scrollview and set scrollEnabled on the outer scrollview to true or false correspondingly.

@smm76 i tried, but the problem persists.

I have the same problem. This has been tested both on Expo and a standalone React Native app, both in appetize emulator and on a Xiaomi Pocophone F1. Same behavior, no horizontal nested scrolling on Android, but works great on iOS

Code here https://snack.expo.io/@esbenvb/scrollview-tests

Android behavior https://vimeo.com/322265966

iOS behavior https://vimeo.com/322266040

  React Native Environment Info:
    System:
      OS: macOS 10.14.3
      CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
      Memory: 331.47 MB / 16.00 GB
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 10.12.0 - /usr/local/bin/node
      Yarn: 1.10.1 - /usr/local/bin/yarn
      npm: 6.4.1 - /usr/local/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.1, macOS 10.14, tvOS 12.1, watchOS 5.1
      Android SDK:
        API Levels: 23, 25, 26, 27, 28
        Build Tools: 26.0.2, 26.0.3, 27.0.3, 28.0.2, 28.0.3
        System Images: android-25 | Google Play Intel x86 Atom, android-26 | Google Play Intel x86 Atom, android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom
    IDEs:
      Android Studio: 3.3 AI-182.5107.16.33.5199772
      Xcode: 10.1/10B61 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.6.3 => 16.6.3 
      react-native: 0.58.6 => 0.58.6 

@fabriziobertoglio1987 there was no problem, I just closed it as there was no interest from maintainers 😄 Feel free to resurrect the PR on your own if you think it’s worth the effort.

@scousino relooking at this 100% agree. Thanks for flagging. It should be fixed in core.

For the brave hearts who fight with this issue, here’s my workaround.

I have a horizontal <FlatList /> (parent) and a horizontal <ScrollView /> (child).

On my inner ScrollView (child) I am disabling the <FlatList /> (parent) scroll onTouchStart and I am enabling it back onScrollEndDrag, similarly like @smm76 suggested. But additionally, I’m doing this throttle magic, which kind of solves all the little edge cases when the user doesn’t immediately start dragging.

import throttle from 'lodash/throttle';

...

<ScrollView
  horizontal={true}
  scrollEventThrottle={16}
  // ... bla bla

  onTouchStart={() => {
    if (Platform.OS === 'android') {
      this.props.onTouchStartDisableScrollOnAndroid(); // disables parent scroll

      // If the `onScrollEndDrag` event is not fired almost immediately
      // after this one (and user doesn't start dragging)
      // fire up `onTouchEndEnableScrollOnAndroid` and enable back
      // the parent horizontal view scroll.
      // This may happen when the user quickly taps on the component
      // but doesn't start dragging.
      this.throttledOnScrollEndDragOnAndroid = throttle(
          this.props.onTouchEndEnableScrollOnAndroid,  // enables parent scroll
          300,
          {
              leading: false,
          },
      );
      this.throttledOnScrollEndDragOnAndroid();
    }
  }}
  onScrollEndDrag={() => {
    if (Platform.OS === 'android') {
      this.props.onTouchEndEnableScrollOnAndroid(); // enables parent scroll

      if (this.throttledOnScrollEndDragOnAndroid) {
          this.throttledOnScrollEndDragOnAndroid.cancel();
      }
    }
  }}
>
  /* content */
</ScrollView>

@zhongwuzw , can you please re-open this issue or please tell any other collaborator to re-open it, since the @react-native-bot closed it.

For the ScrollViews to work as expected, nested or otherwise, you need to specify their boundaries. Boundaries are set in “style” property of the ScrollViews.

import { ScrollView } from 'react-native'

//
<ScrollView 
     style={{ 
           width: "equal-to-or-less-than viewport width", 
           height: "equal-to-or-less-than viewport height"
     }}
    contentContainerStyle={{"..."}}
    horizontal
>
    <ScrollView 
           style={{
              width: "lower-than Parent-ScrollView width",
              height: "lower-than Parent-ScrollView height"
           }} 
           contentContainerStyle={{"..."}}
    >
    </ScrollView>
</ScrollVIew>

NOTE: Always Set explicit boundaries(width/height) for Scrollview. So basically all sides of the border have to appear on the screen

I re-opened the PR with the permission of @isnotgood

I have tried to use ScrollView from react-native-gesture-handler and add disableIntervalMomentum props, then it worked!

import { ScrollView } from 'react-native-gesture-handler'

<ScrollView horizontal disableScrollViewPanResponder>
    <ScrollView horizontal />
</ScrollVIew>

Encountering the same problem in react native 0.66.4. Any fix planned?

Need to fix nested horizontal scroll on Android in the core. ScrollView from react-native-gesture-handler works perfect!

I found an easy workaround!

If we use the ScrollView from react-native-gesture-handler everything works as expected.

import { ScrollView } from 'react-native'
import { ScrollView as GestureHandlerScrollView } from 'react-native-gesture-handler'

<ScrollView horizontal>
    <GestureHandlerScrollView horizontal />
</ScrollVIew>

You can have nested horizontal FlatLists like this:

<FlatList {...props} renderScrollComponent={p => <GestureHandlerScrollView {...p} />} />

Updated the snack with this workaround: https://snack.expo.io/@brunolemos/nested-scrollview

gif-android-working

Maybe someone can check what react-native-gesture-handle is doing and bring the same workaround to the core of react-native.

Appreciate the solution! This issue is a bit dated, if anyone still has this issue and none of these solutions work, feel free to open a new issue.

same here

@brunolemos the scroll is working but when the child’s scrollview reach end the parent’s scroll will not fire. You can see it in your expo example also!

horizontal scroll

Same problem for me, with:

<FlatList horizontal nestedScrollEnabled={true}>
    <ScrollView horizontal nestedScrollEnabled={true}>
    </ScrollView>
</FlatList>

And when you try to scroll the child horizontally it scrolls the parent 😦

I just upgrade RN version to 0.73.1 and checked, but still horizontal doesn’t work for the nested ScrollView in my code. Anyone has found it works horizontally?

i found this problem when we use nested scrollView to prevent horizontal or vertical scroll error but its doesnt work in android .so i make it like this (sry for my english )

> - [ ] <scrollView vertical>
> - [ ] <scrollView horizontal >  <--- this scrollview component is not using just prevent scrollview same props error (so just add scrollEnabled to false ) thats it no need to add     nestedScrollEnabled
> - [ ] <scrollView vertical>
> - [ ] 
> - [ ] </scrollView vertical>
> - [ ] </scrollView horizontal>
> - [ ] </scrollView vertical>

Same here, this worked for me.

@brunolemos your solution is working on snack but it is not working on emulator or real devices

@andreicoman11 @makovkastar @aamalric @astreet I checked the file blame and noticed you contributed a lot to the ReactHorizontalScrollView.java file. I was wondering (hoping) if you have any insight of what could be causing the issue above and what the fix could look like?