ListView grid in React Native

ListviewReact Native

Listview Problem Overview


I'm building a simple app in React Native that fetches listings from a remote JSON source and displays them on screen.

So far, using the excellent example here, I've managed to get the results to display using the ListView components in rows (i.e. 1 result per row, see screenshot). I need the results to display in a grid, i.e. 3 to 6 items per row, depending on the screen size and orientation.

What is the best way to get these results into a grid? Can I use ListView for this, or is that only for one-per-row results? I've tried playing around with flexbox styles but, since React doesn't seem to accept % values and ListView doesn't accept styles, I haven't yet had any success.

This is how my listings are displaying at the moment - one per row.

Listview Solutions


Solution 1 - Listview

You need to use a combination of flexbox, and the knowledge that ListView wraps ScrollView and so takes on its properties. With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.

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>}
        />
      );
    }
});

Just a ListView with some dummy data. Note the use of contentContainerStyle. Here's the style object:

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

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

Screenshot

Solution 2 - Listview

ListView is now deprecated and you should use FlatList instead. FlatList has a prop called numColumns which is exactly what we want to create a scrollable grid.

For example:

const data = [
  {id: 'a', value: 'A'},
  {id: 'b', value: 'B'},
  {id: 'c', value: 'C'},
  {id: 'd', value: 'D'},
  {id: 'e', value: 'E'},
  {id: 'f', value: 'F'},
];
const numColumns = 3;
const size = Dimensions.get('window').width/numColumns;
const styles = StyleSheet.create({
  itemContainer: {
    width: size,
    height: size,
  },
  item: {
    flex: 1,
    margin: 3,
    backgroundColor: 'lightblue',
  }
});

function Grid(props) {
  return (
    <FlatList
      data={data}
      renderItem={({item}) => (
        <View style={styles.itemContainer}>
          <Text style={styles.item}>{item.value}</Text>
        </View>
      )}
      keyExtractor={item => item.id}
      numColumns={numColumns} />
  );
}

enter image description here

This blog post does a good job explaining the new features of FlatList.

Note: For some reason you have to use keyExtractor on the FlatList instead of the typical key prop on each item. Otherwise you'll get a warning. https://stackoverflow.com/questions/44545148/basic-flatlist-code-throws-warning-react-native

Solution 3 - Listview

I'm adding this as an answer because overflow comments are hidden under Colin Ramsay's response: as of React Native 0.28, you also need alignItems: 'flex-start', in the listview styling or the flexWrap won't work. Thanks to Kerumen on codedump.io for this. So,

var styles = StyleSheet.create({
list: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    alignItems: 'flex-start',
},

...with the rest as in Colin's response.

Solution 4 - Listview

I had the same problem and I wrote a component that solves that problem, you can find it here: https://github.com/pavlelekic/react-native-gridview

Also, one other thing this component does is it makes sure that the items width is a whole number, so that the borders of items don't have antialiasing, they are clear and crisp.

Solution 5 - Listview

Follow Colin Ramsay's answer. And if you want half width for each item, try this way.

...
import { Dimensions } from 'react-native'; 
const { width, height } = Dimensions.get('window');
const gutter = 0; // You can add gutter if you want
...


const styles = StyleSheet.create({
  item: {
    width: (width - gutter * 3)/2,
    marginBottom: gutter,
    flexDirection: 'column',
    alignSelf: 'flex-start',
    backgroundColor: '#ff0000',
  },
  list: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    flexWrap: 'wrap',
    paddingHorizontal: gutter,
  },
});

Solution 6 - Listview

use FlatList instead of Listview in react-native. it has more value added feature and better performance. checkout the example here

Solution 7 - Listview

You can use FlatList and set numColumns to obtain the result which is the same with grid

Solution 8 - Listview

Check the following example using FlatList component of React Native to support infinite scrolling with an excellent performance:

//Resize the grid
onLayout = (event) => { 
  const {width} = event.nativeEvent.layout;
  const itemWidth = 150
  const numColumns = Math.floor(width/itemWidth)
  this.setState({ numColumns: numColumns })
}

render() {
  return (
    <Content 
     contentContainerStyle={{flex:1, alignItems: 'center'}}
     onLayout={this.onLayout}>
       <FlatList
        data={this.state.products}
        keyExtractor={(item, index) => index}
        key={this.state.numColumns}
        numColumns={this.state.numColumns}
        renderItem={({item}) => 
          <ListThumb //Custom component, it's only an example.
           navigation={navigation}
           brand={item.brand}
           price={item.price}
           imageSource={item.image_link}/>
        }
     />
   </Content>
  )
}    

The example looks like

enter image description here

Best regards, Nicholls

Solution 9 - Listview

ScrollView with contentContainerStyle prop worked for me

        <ScrollView contentContainerStyle={{ flexDirection: "row", flexWrap: "wrap", alignContent: "center",}}>
			{user.posts.map((post, index) => (
				<Image
					source={require(post.uri)}
					key={index}
					style={{ width: 100, height: 100 }}
				/>
			))}
		</ScrollView>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMateoView Question on Stackoverflow
Solution 1 - ListviewColin RamsayView Answer on Stackoverflow
Solution 2 - ListviewTim PerkinsView Answer on Stackoverflow
Solution 3 - ListviewkwishnuView Answer on Stackoverflow
Solution 4 - ListviewPavle LekicView Answer on Stackoverflow
Solution 5 - ListviewYi Feng XieView Answer on Stackoverflow
Solution 6 - ListviewMoorthyView Answer on Stackoverflow
Solution 7 - ListviewnguyencseView Answer on Stackoverflow
Solution 8 - ListviewjdnichollscView Answer on Stackoverflow
Solution 9 - Listviewanto004View Answer on Stackoverflow