React Native FlatList last item visibility issue

89.1k Views Asked by At

I am fetching products list and then displaying using a FlatList, my list contains 5 items and as you can see FlatList row height is variable because of varying description text. So the issue is my last item card is not completely visible maybe this is some kind of flat list issue or layout issue. Any help would be highly appreciated

 renderProducts() {
        if (this.props.loading === true) {
            return (
                <View style={Styles.spinnerStyle}>
                    <ActivityIndicator size='large' />
                </View>
            );
        }

        return (
                <FlatList
                    data={this.props.myProducts}
                    keyExtractor={(item) => item.id}
                    renderItem={({ item }) => (
                        <Card 
                            title={item.title} 
                            image={{ 
                                uri: item.image !== null ? item.image.src :'../resImage.jpg' 
                            }}
                        >
                            <Text style={{ marginBottom: 10 }}>
                                {item.body_html}
                            </Text>
                            <Button
                                icon={{ name: 'code' }}
                                backgroundColor='#03A9F4'
                                fontFamily='Lato'
                                buttonStyle={{ borderRadius: 0, marginLeft: 0, marginRight: 0, marginBottom: 0 }}
                                title='VIEW NOW' 
                            />
                      </Card>
                      )}
                />
        );
    }
    
    render() {
        return (
            <View>
                <View style={Styles.viewStyle}>
                    <Text style    {Styles.textStyle}>ProductsList</Text>
                </View>
                    { 
                        this.renderProducts() 
                    }
            </View>
        );
    }
20

There are 20 best solutions below

0
On

wrap flatList with view tag, then style view tag with flex:1. it will work.

2
On

This work very well in my case:

  <FlatList
    data={todos}
    contentContainerStyle={{ height: '100%' }}
    renderItem={({ item }) => <Todos items={item} pressed={pressed} />}
  />
0
On

I've solved it doing contentInset={{ bottom: data.length * itemStyle.height, }} with itemStyle.height being 50 worked fine.

0
On

I was seeing this same problem in our Android + iOS React Native hybrid app. We embed the FlatList component within our native UIs inside a Fragment in Android and we were unable to scroll to the last item in the list, even though the scroll indicator would show that there was more to scroll, the ScrollView would simply not scroll further. I tried all the combinations of using a wrapping <View style={{flex:1}}> to wrap the FlatList as well as using contentContainerStyle={{flexGrow:1}} on the FlatList without success. Pursuing the clue further it turned out that the FlatList needs an absolute, predefined height on Android to allow scroll to the bottom - it works just fine on iOS but on Android using match_parent wasn't going to work. Since we need to support all types of devices, phone and tablet too, it wasn't possible to pre-define an absolute height either.

To fix this, I made a custom FrameLayout subclass to house the ReactRootView's fragment, which overrides onLayout() to ignore the child view measurements, forcing the views to have the exact dimensions of the FrameLayout, somewhat like so in Kotlin:

class StretchFrameLayout @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        for (child in children){
            if (child.visibility == View.GONE) continue
            child.updateLayoutParams {
                this.width = measuredWidth
                this.height = measuredHeight
            }
            if (needsRelayout){
                handler.postDelayed({child.requestLayout()},1)
            }
        }
        super.onLayout(changed, left, top, right, bottom)
    }
}
0
On

@krish solution is great for the fixed-size list items, however as @Neeraj Sewani said, it may not be suitable for dynamic size list items.

so you can fix the issue like this -in case direction is column -:

<View  style={{height: '90%'}}> 
<FlatList/> 
</View>

Otherwise, -in case direction is row -:

<View  style={{height: '90%', width:'90%'}}> 
<FlatList/> 
</View>
4
On

Add {flex: 1} to the View tag housing the Flatlist component.

In my case,

const App = () => {
  return (
    <Provider store={createStore(reducers)}>
    <View style={{ flex: 1 }}>
      <Header headerText={'My App'} />
      <ScreenTabs /> // this is my content with FlatList 
    </View>
    </Provider>
  );
};

export default App;
0
On

use contentContainerStyle props of FlatList

<FlatList contentContainerStyle={{ paddingBottom: 20}} />
0
On

Work very well for me

<FlatList
  data={data}
  contentContainerStyle={{ paddingBottom: 30 }}
  style={{height: '95%'}}
  renderItem={({ item, index }) => (
    <ListItem item={item} onPress={() => handlePress(item, index)} />
  )}
/>
4
On

Latest update:

react-navigation has a SafeAreaView with an option to not show that bottom area.

import { SafeAreaView } from 'react-navigation';
<SafeAreaView forceInset={{ bottom: 'never' }} />

Old response below:

You can't see your list with flex: 1 because flex: 1 will grow the component to the parent. If the parent doesn't have flex: 1, it won't stretch to its parent or the screen. Keep in mind, however, that flex: 1 with a SafeAreaView will cause the bottom safe area to show. This will look bad if your SafeAreaView backgroundColor is a different color from your list's back ground.

My old workaround was to add an item to the bottom of the array of items, but I'm still exploring how to scroll past/under the bottom safe area margin with a FlatList (which is how I found this post to begin with).

Update: Using ListFooterComponent you can create even a plain white "footer" with height and/or a margin

For example (I wouldn't directly copy and paste this if I were you... there's surely a better way to detect bezel-less iPhones, especially in 2019 when we have more than one)

ListFooterComponent={<View style={{ height: 0, marginBottom: 90 }}></View>}

This is how I would do it, using the iPhoneX's height for now. But it's not future-proof since the conditional will need to be updated every time a new iPhone with no bezels comes out:

ListFooterComponent={<View style={{ height: 0, marginBottom: noBezels ? 90 : 0 }}></View>}

Or you could just always have some spacing at the bottom, like a loading gif, a message... whatever.

UPDATE 2:

I found out about react-native-device-info which has a hasNotch() method. I find that useful for styling for iPhones with no bezels by combining hasNotch() with Platform.OS === 'ios'

2
On

For IOS issues you can apply some IOS specific props:

<FlatList
  // ...
  contentInset={{top: 0, bottom: 20, left: 0, right: 0}}
  contentInsetAdjustmentBehavior="automatic"
  // ...  
/>

The solution with contentContainerStyle padding didn't seem the best overall for fixing the safe area IOS issues in my case.

4
On

Set bottom padding to the <FlatList> content container:

<FlatList
    contentContainerStyle={{ paddingBottom: 20 }}
/>
0
On

For me, it worked when I used a mix of the above answers

First of all

<FlatList
    contentContainerStyle={{ paddingBottom: 20 }}
/>

and secondly, the parent View of FlatList should have flex: 1

So it looks like this

<View style={{flex: 1}}>
   <FlatList
       contentContainerStyle={{ paddingBottom: 20 }}
   />
</View>
2
On

Just wrap it in a view with flex:1

<ParentView style={{flex:1}
    <View style={{flex:1}}>
    // Your flatlist
    <View>
</ParentView>

Also, note that the each parent of this "View" in which Flatlist is wrapped must also be a View with Flex of 1. Otherwise, that your flatlist wont be visible.

0
On

For dynamic flatlist, you can assign height to the parent view. I resolved it with same.

<View style={{height:'80%'}}>
  <Flatlist
    extraData={data}
    data={data}
    renderItem={renderItem}
  />
</View>
0
On

I had the same issue and found the solution. To fix the issue just add style={{flex: 1}} for each View element who is a parent for FlatList.

See updated code below.

    render() {
        return (
            <View style={{flex: 1}}> // Here you should add style as {flex: 1}
                <View style={Styles.viewStyle}>
                    <Text style={Styles.textStyle}>ProductsList</Text>
                </View>
                { this.renderProducts() }
            </View>
        );
    }
0
On

Adding a 'flexGrow: 1' fixed my issue:

<FlatList
  contentContainerStyle={styles.container}
  // ...
/>

style:

container: { alignItems: 'center', flexGrow: 1 },

0
On

This worked for me.

<View style={{flex: 1}}>
    <FlatList
      style={{flex: 1}}
      data={data}
      renderItem={({item}) => (
        <ListItem item={item} onPress={() => handlePress(item)} />
      )}
    />
  </View>
0
On

Give a margin to the bottom of the flatlist.

<FlatList contentContainerStyle={{ marginBottom: 50 }} />

0
On

Make use of the contentContainerStyle prop in the flatlist <FlatList contentContainerStyle={{paddingBottom: 10}} />

0
On

You can try this solution

For Vertical FlatList:

<FlatList
 ListFooterComponent={<View />}
 ListFooterComponentStyle={{height:200}}
/>

For Horizontal FlatList:

<FlatList
  contentContainerStyle={{paddingRight:40}}
/>