I am using a horizontal FlatList as menu bar for my application. The FlatList must scroll to the clicked item and I am using initialScrollIndex for this.
But the problem is that it scrolls but the content is not visible many times.
My code
<FlatList
ref={listRef}
data={headerMenuChannels}
style={{
backgroundColor: applicationTopBar.buttonBarColor,
width: Dimensions.get('window').width,
}}
showsHorizontalScrollIndicator={false}
horizontal={true}
onScrollToIndexFailed={error => console.log('scroll failed', error)}
initialScrollIndex={screenNumber}
renderItem={v => (
<View
key={v.index}
onLayout={ref => onMenuLayout(ref, v.index)}>
<Text
onPress={() => {
setScreenNumber(v.index);
}}>
{v.item}
</Text>
</View>
)}
/>
Video of the problem
here
With initialScrollIndex you should use getItemLayout prop. Something like this:
getItemLayout={(data, index) => ({
length: width,
offset: width * index,
index,
})}
Related
I have a horizontal FlatList where I render a collection, now I want to know which item is into view (index) taking into account that each item width is almost equal to device width, lets say 400, how can I get visible item index on my onScroll method?
<FlatList scrollEventThrottle={16} onScroll={handleScroll} showsHorizontalScrollIndicator={false} style={{ width:'100%', '}}
horizontal={true} data={categories} keyExtractor={item => item.toString()}
renderItem={({ item, index }) =>
<TouchableOpacity style={{ width:400,height:275 }}>{ item }</Text>
</TouchableOpacity>}
/>
handleScroll method:
const handleScroll = (event) => {
console.log(event.nativeEvent.contentOffset.x);
};
You can use FlatList onViewableItemsChanged prop to get what you want.
const _onViewableItemsChanged = React.useCallback(({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems);
console.log("Changed in this iteration, ", changed);
}, []);
const _viewabilityConfig = {
itemVisiblePercentThreshold: 60
}
<FlatList
scrollEventThrottle={16}
showsHorizontalScrollIndicator={false}
style={{ width: '100%' }}
horizontal={true}
data={categories}
onViewableItemsChanged={_onViewableItemsChanged}
viewabilityConfig={_viewabilityConfig}
keyExtractor={item => item.toString()}
renderItem={({ item, index }) =>
<TouchableOpacity style={{ width: 400, height: 275 }}><Text>{item}</Text>
</TouchableOpacity>}
/>
viewabilityConfig can help you to make whatever you want with viewability settings. In code example 60 means that item is considered visible if it is visible for more than 60 percents.
Displaying more than 200+ images in the flatlist and make the user to select or deselect by clicking an image. So, when the user is clicks a image, Need to show the check-circle icon on the image. Similarly, when the user is deselect the selected image, need to remove the icon. I got two arrays images[] and selectedImagesId[]. Whenever the user selects an image, the image id will be pushed into selectedImagesId array. By the way, Iam showing the icon if the rendered image id avaliable in the selectedImagesId. The problem here is, it takes so long to display or remove the icon.
<FlatList
data={props.images}
extraData={selectedImagesId}
initialNumToRender={10}
refreshing={true}
removeClippedSubviews={true}
maxToRenderPerBatch={1}
windowSize={7}
showsVerticalScrollIndicator={false}
numColumns={3}
keyExtractor={(item) => item.id.toString()}
renderItem={(itemData) => <RenderData itemData={itemData} />}
>
const RenderData = ({ itemData }) => (
<View>
<TouchableOpacity
activeOpacity={0.8}
style={{
width: width / 3.5,
height: height / 7,
padding: 2,
}}
onPress={() => {
console.log('pressed');
if (props.selectedImages.includes(itemData.item.uri)) {
const index = props.selectedImages.indexOf(itemData.item.uri);
if (index > -1) {
props.removeImageandId(index, itemData.item.id);
}
} else {
props.selectImageandId(itemData.item.uri, itemData.item.id);
}
}}>
<Image
style={{ width: '100%', height: '100%' }}
source={{
uri: itemData.item.uri,
}}
/>
{selectedImagesId.includes(itemData.item.id) && (
<AntDesign
name='checkcircle'
size={24}
color='white'
style={{ position: 'absolute', bottom: 5, right: 15 }}
/>
)}
</TouchableOpacity>
<Text style={{ backgroundColor: 'red', fontSize: 25 }}>
{render.current++}
</Text>
</View>
);
You can Refer This article
It will guide you on how to render Only particular selected components that change.
not the Whole flatList components.
so, that based Only selected particular component you can remove the icon. not whole flatList Re-render.
I'm listing data with flatlist. But while listing the data, it lists it vertically, I want it to be sorted horizontally. So instead of scrolling down, I want to scroll left and right. I think it's called pagingEnabled. How can I do that?
<FlatList
style={{
flex: 1
}}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
data={this.state.allReasons}
keyExtractor={(item,index)=>index.toString()}
renderItem={({ item ,index}) => {
return (
<ScrollView onScroll={(event)=>{
this.setState({
activePage:Math.round(parseFloat(event.nativeEvent.contentOffset.x/Dimensions.get('window').width))
})
}}
style={{width:'100%'}} showsHorizontalScrollIndicator={false} pagingEnabled={true} horizontal={true}>
<View style={{width:'100%'}}>
<Image style={{ resizeMode: 'contain',width:Dimensions.get('window').width,height:250}} source={{uri:"http://ledslider.stechomeyazilim.com/" + item.Slider_Image}}/>
.....
</View>
Add horizontal prop in Flatlist like
horizontal={true}
Try removing scrollview from item may cause the issue
I've got (what I thought was) a simple FlatList which renders a list of Cards (code below)
Problem: the list renders, but won't scroll to fully display the last element in the list, OR to the content below the FlatList
What I've tried: basically everything in related SO questions:
Removing ALL styling
Wrapping the FlatList in a View or a ScrollView or both
Adding style={{flex: 1}} to the FlatList or wrappers (this causes **ALL* content to disappear)
Any ideas?
<FlatList
data={props.filteredProducts}
renderItem={({item}) => (
<TouchableOpacity onPress={() => props.addItemToCart(item)}>
<Card
featuredTitle={item.key}
image={require('../assets/icon.png')}
/>
</TouchableOpacity>
)}
keyExtractor={item => item.key}
ListHeaderComponent={
<SearchBar />
}
/>
...
<Other Stuff>
Add style={{flex: 1}} for each View element who is a parent for FlatList.
I had the same issue and just found the solution. Add the prop ListFooterComponentby giving it a React element/component. I just passed a view with the preferable height and it worked for me perfectly.
Here is the code snippet:
<FlatList
data={DATA}
renderItem={({ item }) => (<ListItem itemData={item} />) }
keyExtractor={item => item.id}
ListFooterComponent={<View style={{height: 20}}/>}
/>
This works 100%!
The ultimate solution is ✔
Any view/subview that holds a flatlist must have a flex of 1 - Regardless of how deep.
So if you apply this and it's not working, check your styling and make sure there's no mistake somewhere.
I'm not sure if your scenario is exactly the same as one I encountered on a project a few months ago, but I noticed that I had to add a margin/padding (depends on what you prefer) to lift the bottom of the scrollable container. This was usually because a parent container seemed to interfere with the styling of the scrollable element.
Have you tried adding flexGrow: 1 to your styling in place of flex?
My case is a little different, in that I used FlatList inside bottom sheet provided by reanimated-bottom-sheet package. But the problem was the same: scrolling didn't show the last item properly.
The way I did was I calculated and set the height of the view that included the FlatList. To make it look better when there is bottom inset, I gave more bottom margin to the last item of the FlatList by doing like this:
<FlatList
data={results}
keyExtractor={(item) => item.name}
scrollEnabled={bottomSheetIndex == 1 ? true : false}
renderItem={({ item }) =>
<LocationInfo
bottom={results[results.length - 1].id == item.id ? insets.bottom : 0}
result={item}
wait={waitState[item.id]}
bsIndex={bottomSheetIndex}
/>
}
/>
const LocationInfo = ({ bottom, result, wait, bsIndex }) => {
return (
<View style={[styles.container, { paddingBottom: bottom }]}>
...
for insets, see react-native-safe-area-context.
Remove flex:1 from the FlatList and leave only the parent View.
Add flex: 1 to child into renderItem
<View style={{ height: `93%` }}>
<FlatList
contentContainerStyle={{ minHeight: `100%` }}
scrollEnabled={true}
...props
renderItem={({item}) => (
<View style={{ flex: 1 }}>
...
</View>
)}
I had the same problem. What I did was to provide a marginBottom to the parent view of the FlatList with size that is equivalent (or a little larger) than the size of the rendered item .
<View style={{marginBottom: HEIGHT_OF_CELL + 60, flexGrow:1}}>
<FlatList
keyExtractor={(item, index) => index.toString()}
data={posts}
renderItem={renderItem}
/>
</View>
I method I used to resolve this is by setting the parent view to the FlatList's background color to a visible color(e.g red). Then adjust the margin bottom for that view so that it is large enough for all the contents in the FlatList to be viewed.
In my case the parent view was wrapped in another view which I could not give the flex: 1 style to.
use dimensions for parent view and set height as given:
const screenHeight = Dimensions.get('window').height - 100
<View style={{...styles.container,height:screenHeight}}>
....All child components
</View>
adding flex:1 to my render item worked for me
<FlatList
data={data}
renderItem={({ item }) => (
<ListItem
onPress={() => console.log('ok')}
bottomDivider
rightTitle={
<Divider style={{ marginBottom: 4, backgroundColor: item.status, width: '50%', height: '11%', borderRadius: 10 }} />
}
titleProps={{ numberOfLines: 1 }}
rightSubtitle={item.date}
rightTitleStyle={{ fontFamily: 'poppins' }}
rightSubtitleStyle={{ fontFamily: 'poppins' }}
titleStyle={{ fontFamily: 'poppins' }}
subtitleStyle={{ fontFamily: 'poppins' }}
title={item.device}
subtitle={item.type}
leftAvatar={{ source: item.icon, rounded: false }}
**style={{ flex: 1 }}**
/>
)}
keyExtractor={item => item._id}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
onEndReached={handleLoadMore}
onEndReachedThreshold={0.2}
/>
Working with React Native, having some issues with the FlatList component.
This is my FlatList
<FlatList
data={this.state._data}
renderItem={() => this.renderItem()}
refreshControl={
<RefreshControl
onRefresh={() => this.handleRefresh}
refreshing={this.state.refreshing}
/>
}
/>
This is my renderItem function:
renderItem({item, index}) {
return (
<View style={{marginTop: 10, marginHorizontal: 10, paddingLeft:
10}}>
<ListItem
roundAvatar
title={`${item.itemName}`}
subtitle={`${item.amount}`}
avatar={require('../../../images/logo.png')}
/>
<View
style={{
paddingBottom: 10,
paddingTop: 10,
display: 'flex',
flexDirection: "row",
justifyContent: "space-around",
alignContent: "center"
}}
>
<View style={{ flexDirection: "row", alignContent:
"center", width:"45%"}}>
<Button
block
small
// disabled={this.state.acceptButtonGray}
style=
{this.state.acceptButtonGray ? ({
backgroundColor: 'gray',
width: "100%"
}) : ({backgroundColor: "#369ecd",
width: "100%"
})}
onPress={() =>
this.setState({
modalVisible: true,
// acceptOrDeclineModalText: `Accept offer for ${item.amount} ${'\b'} Are you Sure?`,
acceptOffer: true,
})
}
>
<Text>
Accept
</Text>
</Button>
</View>
</View>
</View>
);
}
this.setState in the onPress in the button should make a Modal visible, and set acceptOffer to true. Modal opens, user confirms their offer. The offer button which opened that modal now should be gray, and even better, disabled.
Passing my RenderItem function as shown above, I receive
TypeError: Cannot read property 'item' of undefined.
Passing my RenderItem function like this:
renderItem={this.renderItem}
I Get This Error:
_this2.setState is not a function
The FlatList Component is certainly responsible for part of my issue, as well as how and where I am calling this.setState. Only one button is shown in my post, but there are two, one for accept, one for decline. Would having two modals change anything?
The FlatList displays my ListItem components with ease until I attempt to call this.setState in the buttons inside the View which contains those ListItems.
The Modal close button takes this.state.acceptOffer and if true, sets this.state.acceptButtonGray to true, should this logic be somewhere else?
Is there another way to open a modal and change the button color without using component state? Does react want these buttons inside of a TouchableOpacity?
I greatly appreciate any help given.
you should write a renderItem function like this
renderItem = ({item, index}) => {
// return here
}
Change your renderItem method to renderItem={this.renderItem.bind(this)}?
As per my Knowledge item and index are passed as object in flatlist's renderItem
so we can pass by two ways
1 way
Flatlist Component
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => this._renderItem(item, index)} //Passing as object from here.
/>
Render Item
_renderItem = (item, index) => {
console.log(item)
console.log(index)
}
2 way
Flatlist Component
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index.toString()}
renderItem={( item, index ) => this._renderItem(item, index)}
/>
Render Item
_renderItem = ({item, index}) => { // Passing as object from here
console.log(item)
console.log(index)
}
1) You can write function as -
renderItem = ({item, index}) => {
// return here
}
2) or else if you want to execute your function then -
<FlatList
data={this.state._data}
renderItem={(item) => this.renderItem.bind(this, item)}
refreshControl={
<RefreshControl
onRefresh={() => this.handleRefresh}
refreshing={this.state.refreshing}
/>
}
/>
You have to use bind(this,item) or change function like (item)=>.
I experienced the same issue and wasted many hours to figure out why it was not re-rendering:
We need to set extraData prop of FlatList if there is any change in the state like so:
<FlatList data={this.state.data} extraData={this.state} .../>
Please see the official documentation here:
https://facebook.github.io/react-native/docs/flatlist.html