FlatList can't scroll horizontally - react-native

I'm trying to scroll horizontal but Its never move to the second or third item.
When I'm dragging left I cant see part of the second elements.
export const Flat: React.FC = () => {
const aaa = [{}, {}, {}]
const renderItem = ({item, index}: any) => {
if(index === 0){
return <View style={{width:"100%", backgroundColor: "green"}}>
<Text> asdasad </Text>
</View>
}
else if(index === 1){
return <View style={{width: "100%", backgroundColor: "red"}}>
<Text> asdasad </Text>
</View>
}
return <View style={{width: "100%", backgroundColor: "blue"}}>
<Text> asdasad </Text>
</View>
}
return <View style={{flex: 1}}>
<FlatList style={{flex: 1}}
contentContainerStyle={{ flex: 1}}
horizontal={true}
data={aaa}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
pagingEnabled={true}
// bounces={false}
/>
</View>
}

Here is - https://snack.expo.dev/RNeBkS7Rk , I just added <ScrollView horizontal> and change yours styles : width:"100%" to width:300

Related

Why SectionList onEndReached is not working?

When I load my component onEndReached will be triggered one time before I scroll my <SectionList />, but when I scroll to bottom onEndReached won't be triggered any more.
Here is my code:
render (
return (
<View style={{ flex: 1 }}>
<SectionList
style={{ backgroundColor: 'pink' }}
refreshControl={
<RefreshControl
refreshing={false}
onRefresh={() => console.log('refreshControl')}
tintColor='gray'
/>
}
renderSectionHeader={this.sectionHeader}
renderItem={this.renderSectionView}
sections={reservations}
onEndReachedThreshold={0.05}
onEndReached={(data) => {
console.log('data', data); // { distanceFromEnd: -457 }
console.log('onEndReached');
}}
keyExtractor={(item, index) => index}
ItemSeparatorComponent={() => <View style={{ backgroundColor: 'gray', height: StyleSheet.hairlineWidth }} />}
/>
</View>
);
);
Here is my <SectionList /> screen:
In my <SectionList /> refreshControl is fine but onEndReached is not working, any one knows what happened ? Thanks.
Looks like a lot of people are experiencing the same. There is a suggestion to change the onEndReachedThreshold to value bigger that 0.05, for example: 0.5
Try something like
render (
return (
<SafeAreaView style={{ flex: 1 }}>
<SectionList
style={{ backgroundColor: 'pink' }}
refreshControl={
<RefreshControl
refreshing={false}
onRefresh={() => console.log('refreshControl')}
tintColor='gray'
/>
}
renderSectionHeader={this.sectionHeader}
renderItem={this.renderSectionView}
sections={reservations}
onEndReachedThreshold={0.05}
onEndReached={(data) => {
console.log('data', data); // { distanceFromEnd: -457 }
console.log('onEndReached');
}}
keyExtractor={(item, index) => index}
ItemSeparatorComponent={() => <View style={{ backgroundColor: 'gray', height: StyleSheet.hairlineWidth }} />}
/>
</SafeAreaView>
);
);

Horizontal Flatlist Re-Render changes ListHeaderComponent

I'm banging my head on this one. I am using a horizontal flat list. And putting a view in ListHeaderComponent to act as padding. You can see it as a blue box in the first image below. That works great. Trouble is, when I refresh the flatlist from elsewhere in my app, the padding seems to break. See the blue box in image 2 below. Code below images. Any help appreciated.
OnLoad:
After props refresh:
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
horizontal={true}
showsHorizontalScrollIndicator={false}
data={this.props.store.allBookData}
ListHeaderComponent={<View style={{ width: itemOffset / 2, height: 200, backgroundColor: 'blue' }}></View>}
ListFooterComponent={<View style={{ width: itemOffset /2 }}></View>}
renderItem={({ item }) =>(
<TouchableWithoutFeedback onPress={() => this.gotoBook(item)}>
<Animatable.View
animation={"fadeIn"}
easing={"ease-in-out"}
duration={320}
delay={240}
useNativeDriver={true}
style={[styles.bookItem, { width: itemWidth, height: itemHeight, marginTop: itemMarginTop, opacity: 1, marginRight: 24 }]}>
{/* cover */}
<View style={[styles.bookCover, { width: itemWidth, height: bookHeight, backgroundColor: global.setBookItemColor(item.color)}]}>
<View style={styles.bookBindingHolder}>
<View style={styles.bookBinding}></View>
</View>
<View style={styles.bookCoverPhotoHolder}>
<View style={styles.bookCoverPhoto}>
<FastImage style={styles.bookCoverFrame} resizeMode={'contain'} source={Images.coverFrameWithTape}/>
{(() => {
//display cover
if (item.cover == 'temp') {
//temp
return (<FastImage style={styles.bookCoverImg} resizeMode={'cover'} source={Images.tempCover} />)
}else{
//photo
return (
<FastImage
style={[styles.bookCoverImg, {opacity: 0.88}]}
resizeMode={'cover'}
source={{uri: item.cover}}>
</FastImage>
)
}
})()}
</View>
</View>
</View>
{/* title */}
<View style={styles.bookTitleHolder}>
<Text style={[global.TextStyles.h3, global.TextStyles.darkText, global.TextStyles.alignCenter]}>{item.title}</Text>
</View>
{/* count */}
<View style={styles.bookPhotoCountHolder}>
<View style={styles.bookPhotoCount}>
{(() => {
if (item.photoCount == global.maxPhotos){
var itemCount = 'Full'
}else{
var itemCount = item.photoCount + '/' + global.maxPhotos
}
return(
<Text style={[global.TextStyles.lightText, global.TextStyles.smallerLabel]}>{itemCount}</Text>
)
})()}
</View>
</View>
</Animatable.View>
</TouchableWithoutFeedback>
)}/>
It's hard to say where the problem comes from even though there's a good chance it comes from your itemOffset that you apply as width to your HeaderComponent.
However, I have the impression that you can make it easier to define paddings, instead of defining a header and footer component, you could simply apply paddings to the content of your list by passing to your list the props: contentContainerStyle
like that
<FlastList
...
contentContainerStyle={{
paddingHorizontal: itemOffset / 2
}}
/>
but I think you should look in your calcul of itemOffset

Last odd flatlist item stretches along screen

I'm displaying some data items with Flatlist in two columns. But when there is an odd item at the last place, it stretches all along the screen. I don't want that, how can I make it to take only %50 of screen space like even items? Thanks a lot.
CategoryGridTile Component:
return (
<View style={styles.PlaceItem}>
<TouchableCmp onPress={props.onSelect}>
<View>
<View style={{ ...styles.placeRow, ...styles.placeHeader }}>
<ImageBackground
source={{ uri: props.image }}
style={styles.bgImage}
>
<View style={styles.titleContainer}>
<Text style={styles.title} numberOfLines={2}>
{props.title}
</Text>
</View>
</ImageBackground>
</View>
</View>
</TouchableCmp>
</View>
)
}
const styles = StyleSheet.create({
PlaceItem: {
flex: 1,
height: 125,
width: '80%',
backgroundColor: '#f5f5f5',
borderRadius: 5,
overflow: 'hidden',
margin: 6
},
placeRow: {
flexDirection: 'row'
}
})
Screen:
const renderGridItem = itemData => {
return (
<CategoryGridTile
image={itemData.item.image}
title={itemData.item.title}
onSelect={() => {
props.navigation.navigate({
routeName: 'CategoryPlaces',
params: {
categoryId: itemData.item.title,
locationId: locations
}
})
}}
/>
)
}
return (
<FlatList
keyExtractor={(item, index) => item._id}
data={displayedCategories}
renderItem={renderGridItem}
numColumns={2}
/>
)
Changing PlaceItem flex: 1 to flex: 1/2 at CategoryGridTile component solved the problem.

Open a video player in react native

I'm working on an app where I need to display a player with an embed youtube video. I have retrieve the data of a youtube playlist, and put them in a TouchableHighlight. I want now to display a player in a WebView, but just on top of my list. So when I'm clicking on a video, the list go down and a WebView take the place ? Is it possible ? Here is the code :
Fetching data from YTApi
_fetchPlaylist(){
if(this.state.selectedIndex === 0) {
return(
fetch(`https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&maxResults=${results}&playlistId=PLSlVQ0kIy6qx-f5O3J3GwIEOO5Y52z43-&key=${apiKey}`)
.then(res => res.json())
.then(res => {
const videoId = []
res.items.forEach(item => {
videoId.push(item)
})
this.setState({
data:videoId
})
})
.catch(error => {
console.error(error)
})
)
}
}
displayWebView
displayVideo(videoId){
if(this.state.selectedIndex == 0){
return(
<View style = {{height:300}}>
<WebView
style={{flex:1}}
javaScriptEnabled={true}
source={{uri: `https://www.youtube.com/embed?v=x57qZ8Ym51s&list=${videoId}`}}
/>
</View>
)
} else{
return <View></View>
}
}
render
<View style = {styles.containerPlaylist}>
<ScrollView>
<View style = {styles.body}>
{
this.state.data.map((item,i) =>
<TouchableHighlight
key = {item.id.videoId}
onPress = {()=> this.displayVideo(item.id.videoID)}>
<View style = {styles.vids}>
<Image
source = {{uri: item.snippet.thumbnails.medium.url}}
style = {{width: 120, height: 90,marginVertical:30, flexDirection:'column', alignSelf:'flex-start', backgroundColor:'#fff', resizeMode:'contain'}}
/>
<Text style = {styles.vidText}>{item.snippet.title}</Text>
</View>
</TouchableHighlight>
)}
</View>
</ScrollView>
</View>
Thanks
You need to keep webview in your layout and then need to manage two state,
to show and hide webview/video
to change the url of webview/video
Initialize the state like this along with your other state:
this.state = {
showVideo: false,
videoUrl: "default youtube url or empty string"
}
then create your layout like this and adjust your styling as per your need:
<View style={styles.containerPlaylist}>
<ScrollView>
{this.state.showVideo &&
<View style={{ height: 300 }}>
<WebView
style={{ flex: 1 }}
javaScriptEnabled={true}
source={{ uri: this.state.videoUrl }}
/>
</View>
}
<View style={styles.body}>
{
this.state.data.map((item, i) =>
<TouchableHighlight
key={item.id.videoId}
onPress={() => this.displayVideo(item.id.videoID)}>
<View style={styles.vids}>
<Image
source={{ uri: item.snippet.thumbnails.medium.url }}
style={{ width: 120, height: 90, marginVertical: 30, flexDirection: 'column', alignSelf: 'flex-start', backgroundColor: '#fff', resizeMode: 'contain' }}
/>
<Text style={styles.vidText}>{item.snippet.title}</Text>
</View>
</TouchableHighlight>
)}
</View>
</ScrollView>
</View>
and change your function something like this to display video which has been clicked:
displayVideo(videoId) {
this.setState({
videoUrl: `https://www.youtube.com/embed?v=x57qZ8Ym51s&list=${videoId}`
showVideo: true
});
}

react-native: flatlist with dynamic numColumns depending json params

This is my json:
{
data:[
{id:1,type:0...},{id:2,type:0...},{id:3,type:1...},
]
}
and Flatlist
_keyExtractor = (item,index) => item.id.toString();
<FlatList
data={this.state.products}
renderItem={this.renderItem}
keyExtractor={this._keyExtractor}
numColumns={3}
/>
I want something like this:
numColumns={item.type ? 1 : 2}
Is it possible?
Unfortunately, numColumns sets the number of columns for the global FlatList, but it is possible to create a similar behavior in your renderItem function.
Of course it is possible that you have to modify the structure of your data beforehand, so that this workaround is applicable for you.
See example below:
Data:
this.state={
data:[
{id:1,type:0},{id:2,type:1},{id:3,type:1},
{id:4,type:0},{id:5,type:1},{id:6,type:0},
]
}
Render:
const WIDTH = Dimensions.get('window').width; // get the screen width
renderItem({item}){
// if type == 0, render two views side by side
if (item.type == 0){
return(
<View style={{width: WIDTH, flexDirection: 'row'}}>
<View style={{ backgroundColor: 'red', width: WIDTH/2 }}>
<Text> {item.id} a) </Text>
</View>
<View style={{ backgroundColor: 'blue', width: WIDTH/2 }}>
<Text> {item.id} b) </Text>
</View>
</View>
);
}else{
// otherwise render only one item
return (
<View style={{ backgroundColor: 'green', width: WIDTH }}>
<Text> {item.id} </Text>
</View>
);
}
}
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.data}
keyExtractor={(item) => item.id.toString()}
renderItem={(item) => this.renderItem(item)}
/>
</SafeAreaView>
);
}
Image: