skip to Main Content

I am using a FlatList to render an array on the screen. It works fine exception for one use case. The issue I am having is that when I have less than 4 items in a row then it renders differently.

Here is an example. I have 2 items in the second row. I would like it to fill the first two columns but it fills the first and third column.

enter image description here

What am I doing wrong here?

const data = [
    {id:1, title: 'Option 1', image: require('../../assets/empty-profile-picture.jpeg')},
    {id:1, title: 'Option 2', image: require('../../assets/empty-profile-picture.jpeg')},
    {id:2, title: 'Option 3', image: require('../../assets/empty-profile-picture.jpeg')},
    {id:3, title: 'Option 4', image: require('../../assets/empty-profile-picture.jpeg')},
    {id:4, title: 'Option 4', image: require('../../assets/empty-profile-picture.jpeg')},
    {id:5, title: 'Option 4', image: require('../../assets/empty-profile-picture.jpeg')}
,  ];


<FlatList
    contentContainerStyle={{ justifyContent: 'space-between' }}
    data={data}
    numColumns={4}
    keyExtractor={(item) => item.id.toString() }
    renderItem={({item, index}) => {
    const lastItem = index === data.length - 1;
    return (
        <View style={{flex: 1, marginBottom: 20 }}>
            <Image style={styles.cardImage} source={item.image} />
            <Text style={styles.title}>{item.title}</Text>
        </View>
    );
    }}
/>

EDIT

Changing the bottom part to this:

<View style={{flex: 1, marginBottom: 20, alignItems: 'flex-end', }}>
      <Image style={styles(theme).cardImage} source={item.image} />
      <Text style={styles(theme).title}>{item.title}</Text>
</View>

does seem to have some effect on the row and look this:

enter image description here

2

Answers


  1. Remove justifyContent: ‘space-between’ inside the contentContainerStyle
    for the spacing style apply on the main view of the return()
    try them may be it’s working as per your requirement.

    <FlatList
       data={data}
       numColumns={4}
       keyExtractor={(item) => item.id.toString() }
       renderItem={({item, index}) => {
       const lastItem = index === data.length - 1;
       return (
           <View style={{flex: 1, marginBottom: 20 }}> //apply spacing style
           <Image style={styles.cardImage} source={item.image} />
           <Text style={styles.title}>{item.title}</Text>
           </View>
       );
       }}
    />
    
    Login or Signup to reply.
  2. The reason why this happens is numColumns={4}. The FlatList component tries to split its data items evenly on 4 columns. The number 6 is not evenly divisible by 4. The remainder is 2, so it makes room for two components that use the same space as the others.

    You can think of it as a grid. First, the number of items are calculated (in this case 6), then it is determined how many rows are needed (in this case 2). In summary, we get a 2x4 grid and each cell in this grid gets the same amount of space. Second, it tries to distribute the actual items evenly among free cells, alternating empty cells and actually occupied cells. If we have 5 items instead of 6, you will notice, that it looks like you want.

    Solution: I haven’t found an elegant solution to this issue, i.e. some simple prop that we could use. However, we can calculate this ourselves and add a dummy view.

    export default function App() {
      const dummyItemCount = array.length % 4
      const [data, setData] = useState(dummyItemCount > 0 ? [...array, ...[...new Array(dummyItemCount).keys()].map((_, idx) => ({id: array.length + idx}))] : array)
    
      return (
        <FlatList
        contentContainerStyle={{ justifyContent: 'space-between' }}
        data={data}
        numColumns={4}
        keyExtractor={(item) => item.id.toString() }
        renderItem={({item, index}) => {
            return (
             item.image ? <View style={{flex: 1, marginBottom: 20 }}>
                {item.image}
                <Text>{item.title}</Text>
              </View> : <View /> 
            );
        }}
      />
      );
    }
    

    I have created a dummy array for testing:

    const array = [
        {id:1, title: 'Option 1', image: <View style={{height: 20, width: 20, backgroundColor: "red"}}/>},
        {id:2, title: 'Option 2', image: <View style={{height: 20, width: 20, backgroundColor: "red"}}/>},
        {id:3, title: 'Option 3', image: <View style={{height: 20, width: 20, backgroundColor: "red"}}/>},
        {id:4, title: 'Option 4', image: <View style={{height: 20, width: 20, backgroundColor: "red"}}/>},
        {id:5, title: 'Option 5', image: <View style={{height: 20, width: 20, backgroundColor: "red"}}/>},
        {id:6, title: 'Option 6', image: <View style={{height: 20, width: 20, backgroundColor: "red"}}/>},
     ];
    

    The result is as follows and works dynamically for any number of elements.

    enter image description here

    Here is a little snack for testing.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search