Reputation: 411
I'm trying to implement a horizontal scrolling list that has two rows. Using FlatList, the vertical scrolling list involves setting numColumns
but there is no equivalent for using rows with horizontal.
I was successfully able to make it render properly, and it works flawlessly. However, a warning gets thrown saying setting flexWrap
is not supported in VirtualizedList
or FlatList
, and to use numColumns. I cannot use numColumns as that is not meant for horizontal lists.
<FlatList
horizontal={true}
contentContainerStyle={{
flexDirection: 'column',
flexWrap: 'wrap'
}}
{...otherProps}
/>
I found the commit where this warning was added, but cannot find the reasoning behind it. There seems to be no way to make this work without a warning being thrown, at least without ditching FlatList entirely. Is there a more appropriate solution for horizontal lists with rows?
References:
Upvotes: 31
Views: 49496
Reputation: 101
if still relevant, you could try something like this.
<FlatList
inverted={true}
contentContainerStyle={{
flexDirection: 'row',
flexWrap: 'wrap-reverse'
}}
{...otherProps}
/>
I don't know why but, when we use flexWrap: 'wrap-reverse', warning is not shown :)
Upvotes: 0
Reputation: 1471
Please do not use horizontal={true}
. For this case
you should use numColumns
equal to the length of data / 2, and add a <ScrollView>
tag. Forcing the number of columns to be half the total will force the list to wrap to the next line.
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
directionalLockEnabled={true}
alwaysBounceVertical={false}
>
<FlatList
contentContainerStyle={{alignSelf: 'flex-start'}}
numColumns={Math.ceil(listData.length / 2)}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
data={listData}
renderItem={({item, index}) => {
//push your code
}}
/>
</ScrollView>
Update 1: edit listData.length / 2
-> Math.ceil(listData.length / 2)
following Alex Aung comment. Thanks @alex-aung
Update 2: edit add directionalLockEnabled={true}
and alwaysBounceVertical={false}
for stoping dragged vertically while dragging. following @amer-nm, Thanks Amer NM
Upvotes: 29
Reputation: 1
Let check it: https://snack.expo.dev/@legiaquan/flatlist-extra-expo?platform=android
FlatListExtra : https://www.npmjs.com/package/flatlist-extra
import { Text, View, StyleSheet } from 'react-native';
import { FlatListExtra } from 'flatlist-extra';
export default function App() {
const data = [
{ idItem: 1, name: 'one' },
{ idItem: 2, name: 'two' },
{ idItem: 3, name: 'three' },
{ idItem: 4, name: 'four' },
{ idItem: 5, name: 'five' },
{ idItem: 6, name: 'six' },
{ idItem: 7, name: 'seven' },
{ idItem: 8, name: 'eight' },
{ idItem: 9, name: 'nine' },
];
const renderItem = ({ item }) => (
<View style={{ width: 100, height: 100, borderWidth: 1 }}>
<Text
style={{
alignSelf: 'center',
}}>
{item.name}
</Text>
</View>
);
return (
<FlatListExtra
data={data}
renderItem={renderItem}
showsHorizontalScrollIndicator={false}
horizontal
numRows={2}
id={'idItem'}
/>
);
}
Upvotes: 0
Reputation: 1447
I created a wrapper for the FlatList with a numRows
prop.
This solution does not use a FlatList inside a ScrollView, as that may nullify some of the FlatList's performance optimisations. Instead it chunks the data and adds column components which can also be styled.
https://www.npmjs.com/package/@idiosync/horizontal-flatlist
Upvotes: 0
Reputation: 1
Easiest way I found (if you wanted x columns, change numColumns to divide data length by x):
return (
<ScrollView horizontal={true} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} >
<FlatList
data = {yourData}
renderItem={renderFunction}
numColumns={Math.ceil(yourData.length / 2)}
scrollEnabled={false}
/>
</ScrollView>
)
Upvotes: 0
Reputation: 179
tuanngocptn's answer works. However, the two rows horizontal list can also be dragged vertically while dragging it horizontaly which is not that great of a UX.
Adding directionalLockEnabled={true}
and alwaysBounceVertical={false}
to the ScrollView will solve this issue
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
directionalLockEnabled={true}
alwaysBounceVertical={false}
>
<FlatList
...
/>
</ScrollView>
Upvotes: 0
Reputation: 1
With some pre-processing work to slice each ROWS_COUNT
into a column
const ROWS_COUNT = 5;
const len = Math.ceil(data.length / ROWS_COUNT );
const cols = () => {
let colsArr = Array(len);
for (let i = 0; i < len; i++) {
colsArr[i] =
<View style={styles.col}>
{Array(ROWS_COUNT ).fill(0).map((_, j) => {
let index = i * ROWS_COUNT + j;
return (index < data.length) ? <NumSquare key={index} num={index} active={index < 23} color={"#44afff"}/> : <></>
})}
</View>
}
return colsArr;
}
const colsArr = cols();
..................................
<FlatList
horizontal
data={colsArr}
contentContainerStyle={styles.contentContainerStyle}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
renderItem={({ item, index }) => item}/>
..................................
const styles = StyleSheet.create({
col:{
flexDirection:"column",
}
});
Result:
Upvotes: 0
Reputation: 61
enhancement the first answer, that's what I did:
const listData = props.data ?? [];
const numColumns = Math.ceil(listData.length / 2);
<ScrollView
horizontal
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ paddingVertical: 20 }}>
<FlatList
scrollEnabled={false}
contentContainerStyle={{
alignSelf: 'flex-start',
}}
numColumns={numColumns}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
data={listData}
renderItem={renderItem}
/>
</ScrollView>
Upvotes: 6
Reputation: 195
I use simple logic see
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
justifyContent: 'center',
paddingHorizontal: 20,
paddingBottom: 70,
}}>
<View>
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
{this.state.interest.length
? this.state.interest.map((d, i) => {
if (i % 2 == 0) {
return null;
}
return (
<Intrest
style={{
height: 40,
paddingHorizontal: 20,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 30,
margin: 2.5,
}}
key={i}
data={d}
add={data => {
this._handleAddtoSelection(data);
}}
remove={data => {
this._handleRemoveFromSelection(data);
}}
active={this._isSelectedTopic(d)}
/>
);
})
: null}
</View>
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
{this.state.interest.length
? this.state.interest.map((d, i) => {
if (i % 2 != 0) {
return null;
}
return (
<Intrest
style={{
height: 40,
paddingHorizontal: 20,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 30,
margin: 2.5,
}}
key={i}
data={d}
add={data => {
this._handleAddtoSelection(data);
}}
remove={data => {
this._handleRemoveFromSelection(data);
}}
active={this._isSelectedTopic(d)}
/>
);
})
: null}
</View>
</View>
</ScrollView>
Output : Output
Upvotes: 3
Reputation: 51
I had the same problem with a list of items (a few hundred) that is not that long and i managed to overcome it by going over my list mapping them to multiple views each one with 2 items one after the other (column using flex)
If your list is not too long you can also use ScrollView
and it supports flexWrap
Upvotes: 1