I saw that React Native offers nested vertical scroll-views from React Native Nested ScrollView Can`t Scroll on Android Device
The problem is that it doesn't seem to work on nested Horizontal ScrollViews on Android. See this code for an example:
https://snack.expo.io/@harrytravelchime/broken-horizontal-scroll
import React from 'react';
import _ from 'lodash';
import { View, ScrollView, StyleSheet, Text, SafeAreaView } from 'react-native';
export default class App extends React.PureComponent {
render() {
return (
<SafeAreaView style={styles.container}>
<ScrollView
style={{ height: '100%', width: '100%' }}
horizontal
nestedScrollEnabled
>
<View style={{ flexDirection: 'row' }}>
<ScrollView
style={{ width: 200, height: '100%' }}
horizontal
nestedScrollEnabled
>
<View style={{ flexDirection: 'row' }}>
{_.times(200, n => (
<View key={1000 + n} style={{ marginRight: 10 }}>
<Text>{1000 + n}</Text>
</View>
))}
</View>
</ScrollView>
{_.times(200, n => (
<View key={n} style={{ marginRight: 10 }}>
<Text>{n}</Text>
</View>
))}
</View>
</ScrollView>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'stretch',
paddingVertical: 50,
},
});
On the other hand, the same code except with vertical scrolling totally works: https://snack.expo.io/@harrytravelchime/working-vertical-scroll
Is there a way to make nested horizontal scrolling work?
One solution that I've come up with is to have a TouchableWithoutFeedback
that tracks any touch by the user within the inner ScrollView. As soon as it detects a touch, it disables scrolling on the outer ScrollView, which would cause the inner ScrollView to receive events.
The main changes from the above code are:
outerScrollViewScrollEnabled
TouchableWithoutFeedback
, change that statescrollEnabled
depend on thatimport React from "react";
import _ from "lodash";
import {
View,
ScrollView,
StyleSheet,
Text,
SafeAreaView,
TouchableWithoutFeedback
} from "react-native";
interface State {
outerScrollViewScrollEnabled: boolean;
}
export default class App extends React.PureComponent {
state = { outerScrollViewScrollEnabled: true };
handleInnerPressIn = () => this.setState({ outerScrollViewScrollEnabled: false });
handleInnerPressOut = () => this.setState({ outerScrollViewScrollEnabled: true });
render() {
const { outerScrollViewScrollEnabled } = this.state;
return (
<SafeAreaView style={styles.container}>
<ScrollView
style={{ height: "100%", width: "100%" }}
horizontal
scrollEnabled={outerScrollViewScrollEnabled}
>
<View style={{ flexDirection: "row" }}>
<ScrollView style={{ width: 200, height: "100%" }} horizontal>
<TouchableWithoutFeedback
onPressIn={this.handleInnerPressIn}
onPressOut={this.handleInnerPressOut}
>
<View style={{ flexDirection: "row" }}>
{_.times(200, n => (
<View key={1000 + n} style={{ marginRight: 10 }}>
<Text>{1000 + n}</Text>
</View>
))}
</View>
</TouchableWithoutFeedback>
</ScrollView>
{_.times(200, n => (
<View key={n} style={{ marginRight: 10 }}>
<Text>{n}</Text>
</View>
))}
</View>
</ScrollView>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
justifyContent: "center",
alignItems: "stretch",
paddingVertical: 50
}
});