Set the number of rows in FlatList dynamically - react-native

I am trying to create an horizontal list of element where the width is fix but the height is dynamic. That means if no more avatars could be fit in a row, a new row should be created.
const DATA = ["cQpBBiCQ3BwCHKXnTJeKt4yQ2", "zHYYvrVPY1edwOrQhQLt6k1", "zHYYvrVPYedwOrQhQLt6k1", "zHYYvrVPY1edwOrQhQLt6k1", "zHYYvrVPedwOrQhQLt6k1", "zHYYvrVPrQhQLt6k1", "zHYYvsdPrQhQLt6k1"];
const renderConfirmedUserAvatar = ({ item }) => {
if (users[item]) {
return (
<View
style={styles.avatarView}>
<Avatar
size={40}
source={{ width: 80, height: 80, uri: users[item].avatar }}
/>
<Text category={'c2'}>{users[item].userName}</Text>
</View>
)
}
};
<View style={styles.confirmedContainer}>
<FlatList
data={DATA}
renderItem={renderConfirmedUserAvatar}
keyExtractor={item => item}
contentContainerStyle={{ alignSelf: 'flex-start' }}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
horizontal={true}
/>
</View>
const styles = StyleSheet.create({
avatarView: {
alignSelf: 'flex-start',
alignItems: 'center',
margin: 7,
},
confirmedContainer:{
width: 200,
flex: 1,
}
});
With the current code the avatars are shown in one row and only three and a half of them are shown, the other are hidden because of the width.
In this case the expected result is to show the avatars in 3 rows, three avatars in the first two rows and one avatar in the last one.

Try adding flexWrap: 'wrap' to the styles of the view which wraps flatlist

Related

React Native: backgroundColor in View-Component is not working

I started to learn React Native two days ago and I am wondering why the backgroundColor "yellow" and "green" are not working. My whole screen is white. I thought that, because I used 3 times a flex: 0.5, that the screen is devided in 3 parts with different backgroundColors. What is the problem here? The dividing in 3 parts seems to work, because the text is, image and button are on the top 1/3, but the second 1/3 and third 1/3 are white.
Here is the code:
console.log("App executed");
return(
<View style={styles.viewStyles}>
<Text style={styles.textStyles} onPress={() => console.log("Text clicked")}>Beispielfüroben</Text>
<TouchableOpacity onPress={() => console.log("Bild clicked")}>
<Image source={require("./assets/books.jpg")} style={styles.logoStyles}></Image>
</TouchableOpacity>
<Button
style={styles.buttonStyles}
title= "Beispielbutton"
onPress={() => Alert.alert("Hallo", "I bims 1 clown",[{text: "hallo", onPress: () => console.log("Hallo clicked")}, {text: "no"}])}
></Button>
<View style={{backgroundColor: "yellow", flex: 0.5}}/>
<View style={{backgroundColor: "green", flex: 0.5}}/>
</View>
)
};
const styles = StyleSheet.create({
viewStyles: {
backgroundColor: "white",
flex: 0.5,
alignItems: 'center',
justifyContent: 'center'
},
Can you help me?
Thanks a lot :)
The issue here is that a View, which does not contain any children, won't fill any space unless we are telling it to do so.
The flex property will only
define how your items are going to “fill” over the available space along your main axis. Space will be divided according to each element's flex property.
Hence, let your Views occupy 100% of the available height and width. If the flex property is equal for both views, then the available space will be filled equally.
If you want to let the text, button and touchable opacity, occupy 1/3 of the screen, then you need to put them inside the same parent view with a flex of 1 as well and provide the same height and width percentage (100 in both cases).
export default function App() {
return (
<SafeAreaView style={styles.viewStyles}>
<View style={{ width: '100%', height: '100%', flex: 1, alignItems: 'center' }}>
<Text onPress={() => console.log('Text clicked')}>Beispielfüroben</Text>
<TouchableOpacity onPress={() => console.log('Bild clicked')}>
<Image
source={require('./assets/snack-icon.png')}
style={styles.logoStyles}></Image>
</TouchableOpacity>
<Button
title="Beispielbutton"
onPress={() =>
Alert.alert('Hallo', 'I bims 1 clown', [
{ text: 'hallo', onPress: () => console.log('Hallo clicked') },
{ text: 'no' },
])
}></Button>
</View>
<View
style={{
backgroundColor: 'yellow',
flex: 1,
width: '100%',
height: '100%',
}}
/>
<View
style={{
backgroundColor: 'green',
flex: 1,
width: '100%',
height: '100%',
}}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
viewStyles: {
backgroundColor: 'white',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
logoStyles: {
width: 24,
height: 24,
},
});
The result is as follows
Here is a snack.

I am trying to create slide like TikTok using React Ntaive FlatList & pagingEnabled but autometic top margin is added, can anyone help me

This is what I see when app loaded
This is what happens when I reached to the endstrong text
I am trying to create app using React Native and pagingEnabled, and I want to add swipe up down like TikTok but every time I swipe up some margin constantly added to the top of my container. I don't know why ? Can any one know the reason.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
const Home = () => (
<View style={{ flex: 1, height: height }}>
<FlatList
keyExtractor={(item, index) => 'key' + index}
data={DummyData}
renderItem={({ item, index }) => {
return (
<ViewComponent
index={index}
item={item}
style={{
backgroundColor: index % 2 === 1 ? 'dodgerblue' : 'red',
}}
/>
);
}}
pagingEnabled={true}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
/>
</View>
);
const styles = StyleSheet.create({
container: {
height: height,
width: width,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 20,
},
title: {
fontSize: 22,
color: '#fff',
textAlign: 'left',
marginBottom: 10,
}
})

Style the container of React Native's FlatList items separate from the header / footer?

I have a FlatList using the ListHeaderComponent and ListFooterComponent.
Is there a way to style a container of the items (which come from the data prop), but not include the header and footer with in?
https://snack.expo.io/#jamesweblondon/bold-pretzel
import React from "react";
import { View, Text, FlatList } from "react-native";
const exampleData = [...Array(20)].map((d, index) => ({
key: `item-${index}`,
label: index,
backgroundColor: `rgb(${Math.floor(Math.random() * 255)}, ${
index * 5
}, ${132})`,
}));
const Example = () => {
const renderItem = ({ item }) => {
return (
<View
style={{
flexDirection: "row",
width: "100%",
backgroundColor: item.backgroundColor,
}}
>
<Text
style={{
fontWeight: "bold",
color: "white",
fontSize: 32,
height: 100,
}}
>
{item.label}
</Text>
</View>
);
};
return (
<View style={{ flex: 1 }}>
<FlatList
data={exampleData}
renderItem={renderItem}
keyExtractor={(item) => item.key}
ListHeaderComponent={
<View
style={{
backgroundColor: "grey",
height: 200,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Before list</Text>
</View>
}
ListFooterComponent={
<View
style={{
backgroundColor: "grey",
height: 200,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>After list</Text>
</View>
}
/>
<View
style={{
backgroundColor: "gold",
height: 200,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Footer</Text>
</View>
</View>
);
};
export default Example;
Currently it looks like this:
Id like an element allowing me to wrap data so I can add padding, border, etc:
You can use columnWrapperStyle prop instead of contentContainerStyle prop of FlatList. This will help you to make styling of the wrapper of the components generated from the data.
For demo just add border property and you will see this will only apply styles to the container of items and not to ListHeaderComponent and ListFooterComponent
Example
<FlatList
....
columnWrapperStyle={{borderWidth: 1, borderColor: 'red'}}
/>
Note: Please make sure as the name of the prop suggests, the style will be applied to each colum. Also if this prop does not work for you then consider using numColumns prop of FlatList first then apply style with columnWrapperStyle
Check out the contentContainerStyle prop in FlatList, it should help you do exactly what you are looking for.
I have found a workaround that works for me, but I'm not sure if it can cause performance issues, so beware.
The idea is to have two FlatLists, where the one containing your actual list is the FooterListComponent of the other one.
N.B. notice how it's not a FlatList wrapped in a ScrollView, which would trigger the Virtualizedlists should never be nested inside plain scrollviews error.
Here's the code
const MyComponent = () => {
return (
<FlatList
data={[]}
renderItem={() => undefined}
ListHeaderComponent={ListHeader} // whatever you want above your list
ListFooterComponent={() => {
return (
<View style={yourContainerStyle}>
<FlatList
data={data}
renderItem={renderItem}
/>
</View>
);
}}
/>)
}

How can I stick ListFooterComponent to the bottom of the screen?

I have a FlatList component, consisting of 3 sections:
<View style={{ flex: 1 }}>
<FlatList
ListHeaderComponent={Comp1}
ListFooterComponent={<Comp2 style={{ flexGrow: 1, justifyContent: 'flex-end' }}/>}
renderItem={Comp3}
contentContainerStyle={{ flexGrow: 1 }}
/>
</View>
By default, the ListFooterComponent will render right after the ListHeaderComponent, if data.length is 0.
I need to render it at the bottom all the time.
One workaround I've found so far is to provide an empty view for ListEmptyComponent. In this case, it looks fine, until I add at least one item - then it sticks to the top again.
Is it possible to attach ListFooterComponent to the bottom by default?
The blue color is the FlatList, the red color - ListFooterComponent
If it needs to be on the bottom of the screen at all times, you can wrap the separate parts in a ScrollView
render() {
return (
<ScrollView style={{flex: 1}}>
<Comp1/>
<FlatList
style={{flex: 1}}
renderItem={Comp3}
/>
<Comp2/>
</ScrollView>
);
}
Before rederising your View, a good idea is to set your height according to the size of the screen. Something like:
const {height} = Dimensions.get ('window');
The View would look like this:
<View style = {{flex: 1, height: height}}>
Add position: 'relative' to the View:
<View style = {{flex: 1, height: height, position: 'relative'}}>
Then add ListFooterComponentStyle to FlatList:
ListFooterComponentStyle = {{
backgroundColor: '# ccc',
position: 'absolute,
width: '100%',
bottom: 0
}}
Show a complete example function component:
const {height} = Dimensions.get('window'); //capture the screen size
return (
<SafeAreaView style={{flex:1,height:height,backgroundColor:'#f5f5f5', position:'relative'}}>
<FlatList
data = {YOUR_DATA}
renderItem = {renderItem}
keyExtractor = {item => item.idItem}
numColumns = {2} // Divide list items into 2 columns (optional)
onEndReached = {LOAD_MORE_DATA}
onEndReachedThreshold = {0.1} //0.1 = 10%
ListFooterComponent = {YOUR_COMPONENT_FOOTER}
ListFooterComponentStyle={{
backgroundColor:'#ccc',
position:'absolute',
width:'100%',
bottom:0
}}
/>
</SafeAreaView>
)
add flexGrow: 1 to contentContainerStyle of Flatlist
add flexGrow: 1 to ListFooterComponentStyle of Flatlist
add flex: 1 and justifyContent: "flex-end" to View of container used in ListFooterComponent
<FlatList
contentContainerStyle = {{flexGrow: 1}}
listFooterComponentStyle = {{flexGrow: 1}}
listFooterComponent = {()=>(
<View style={{
flex:1,
justifyContent: "flex-end"
}}>
...Component you want at bottom
</View>
)}
/>

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