Search code examples
androidreact-nativescrollviewtouchableopacityreact-flexbox-grid

vertical ScrollView with wrapped row in React Native


I'm new to React Native, and struggling to get my ScrollView working.

I'm trying to have a vertically scrollable list of TouchableOpacity components that are arranged in pairs across the page, e.g.

view arrangement

This is how the app looks when I wrap the TouchableOpacity components in a View, but (obviously) it is not scrollable. If I change the View to a ScrollView, the components are no longer arranged correctly, they now look like this:

Broken ScrollView

They are still not scrollable, and also the layout is no longer working correctly.

So far (and there may be a better way of achieving this?) I've attempted to create my layout by setting the container to have flexDirection: row, with wrap turned on, and sizing the sub components appropriately to fit two across the screen.

Approximation of the code i'm using is:

export default class App extends React.Component {
  render() {
    return (
      <ScrollView style={styles.container}>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => doAThing()} style={styles.newButton}>
              <Text style={styles.buttonText}>TouchableOpacity</Text>
          </TouchableOpacity>
      </ScrollView>
    );

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#fff',
      height: 3440,
      minHeight: 3440,
      width: 1280,
      minWidth: 1280,
      flexGrow: 1,
      flexDirection: 'row',
      flexWrap: 'wrap'
  },
    newButton: {
       height: 180,
       width: 620,
        margin: 5,
        borderRadius: 10,
        alignContent: 'center',
        justifyContent: 'center',
      backgroundColor: 'darkblue'
    },
    buttonText: {
      alignSelf: 'center',
        fontSize: 36,
        color: 'white',
    }
});

Solution

  • This view is a Grid View.

    Try this code. (reference)

    var TestCmp = React.createClass({
        getInitialState: function() {
          var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
          var data = Array.apply(null, {length: 20}).map(Number.call, Number);
          return {
            dataSource: ds.cloneWithRows(data),
          };
        },
    
        render: function() {
          return (
            <ListView contentContainerStyle={styles.list}
              dataSource={this.state.dataSource}
              renderRow={(rowData) => <Text style={styles.item}>{rowData}</Text>}
            />
          );
        }
    });
    

    Here's the style object:

    var styles = StyleSheet.create({
        list: {
            flexDirection: 'row',
            flexWrap: 'wrap'
        },
        item: {
            backgroundColor: 'red',
            margin: 3,
            width: 100
        }
    });
    

    We set items in a wrapping row, and the we set the width of each child object.

    Screenshot

    There are lot of dependencies. That will make your work easy.

    1. https://github.com/GeekyAnts/react-native-easy-grid#readme
    2. https://github.com/idibidiart/react-native-responsive-grid#readme
    3. https://github.com/phil-r/react-native-grid-component#readme
    4. https://github.com/yelled3/react-native-grid-example
    5. https://github.com/toystars/react-native-layout-grid#readme