Xavi A.
Xavi A.

Reputation: 825

2 column layout with flexBox on React Native

I'm trying to do a two column layout in React Native from a list of items. It only seems to work if I define the width of the items, I would like to define just a percentage of the parent width (0.5 would make a 2 column layout, but 0.25 would make a 4 column one). Can this be done?

export default class App extends Component {
  render() {
    return (
      <View style={[styles.container, {width:width}]}>
        <View style={styles.item}><Text>{'item1'}</Text></View>
        <View style={styles.item}><Text>{'item2'}</Text></View>
        <View style={styles.item}><Text>{'item3'}</Text></View>
        <View style={styles.item}><Text>{'item4'}</Text></View>
        <View style={styles.item}><Text>{'item4'}</Text></View>
        <View style={styles.item}><Text>{'item5'}</Text></View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
  item :{
    flex: 0.5, //why this doesnt work???
    // width: 150, //using fixed item width instead of flex: 0.5 works
    height: 100,
    padding: 10,
    backgroundColor: 'red',
    // flexGrow: 1,
    // flexShrink: 0,
  }
});

You can play with it here: https://snack.expo.io/SyBjQuRxm

Css working equivalent: https://codepen.io/klamping/pen/WvvgBX?editors=110

Obviously I could do something like creating a container for each column, but that's not the point:

render() {
    return (
      <View style={[styles.container, {width:width}]}>
        <View style={styles.column1}>
             <View style={styles.item}><Text>{'item1'}</Text></View>
             <View style={styles.item}><Text>{'item2'}</Text></View>
             <View style={styles.item}><Text>{'item3'}</Text></View>
        </View>
        <View style={styles.column2}>
             <View style={styles.item}><Text>{'item4'}</Text></View>
             <View style={styles.item}><Text>{'item4'}</Text></View>
             <View style={styles.item}><Text>{'item5'}</Text></View>
        </View>
      </View>
    );
  }

Upvotes: 51

Views: 104505

Answers (4)

Roni Castro
Roni Castro

Reputation: 2144

If a nested flatList is being used because of the numberColumns. I'd suggest using a custom Grid component instead of nesting flat lists because of CPU usage will increase nesting scroll views.

Example of performance comparison between using nested flat list VS using custom grid and paginating the list.

enter image description here

Example using nested flatList for getting 2 columns https://snack.expo.dev/@ronicesarrc/flatlist-with-flatlist-grid-nested

Example using custom grid with 2 columns https://snack.expo.dev/@ronicesarrc/flatlist-with-custom-grid

// Custom grid with 2 columns
const ProductGrid = ({ products, numColumns = 2 }) => {
  return (
    <View>
      {products.map((_, index) => (
        index % numColumns === 0 && (
          <View key={index} style={{ flexDirection: 'row' }}>
            {products.slice(index, index + numColumns).map((prod) => (
              <ProductItem key={prod.id} {...prod} />
            ))}
          </View>
        )
      ))}
    </View>
  );
};

Upvotes: 0

reggi49
reggi49

Reputation: 560

you can try scrollview with flatlist. the code below generates 2 columns. if you want 3 columns change numcolumn={data.length/3} etc.

<ScrollView
  horizontal
  showsVerticalScrollIndicator={false}
  showsHorizontalScrollIndicator={false}
  contentContainerStyle={{
    flexDirection: "row",
    flexWrap: "wrap",
  }}
>
  <FlatList
    data={data}
    renderItem={this.renderItem}
    keyExtractor={(item) => `${item.id}`}
    showsHorizontalScrollIndicator={false}
    numColumns={data.length / 2}
  />
</ScrollView>

Upvotes: 7

Arne Jenssen
Arne Jenssen

Reputation: 1323

You could map the list of element into a an array of pairs.

Then render the pairs as a row inside a list

<View className="px-4 ">
  {Buttons.map((row, i) => {
    return (
      <View className="flex-1 flex-row" key={i}>
        {row.map((item, j) => {
          return <ThumbButton key={j} {...item} />;
        })}
      </View>
    );
  })}
</View>

For making the nested array you could use this function https://stackoverflow.com/a/44937519/1807666

function combineTwo(inputArray: Array<object>) {
  var result = [];
  for (var i = 0; i < inputArray.length; i += 2) {    
    result.push([inputArray[i], inputArray[i + 1]]); 
  }
  return result;
}

Upvotes: 1

dentemm
dentemm

Reputation: 6379

It is possible if you use percentage values for the widths:

<View style={styles.container}>
  <View style={styles.item}>
    ...
  </View>
</View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap',
    alignItems: 'flex-start' // if you want to fill rows left to right
  },
  item: {
    width: '50%' // is 50% of container width
  }
})

Upvotes: 138

Related Questions