41

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>
        );
    }
Penny Liu
  • 7,720
  • 5
  • 40
  • 66
Muhammad Talha
  • 453
  • 1
  • 5
  • 7

13 Answers13

88

Set bottom padding to the <FlatList> content container:

<FlatList
    contentContainerStyle={{ paddingBottom: 20 }}
/>
Rafael Tavares
  • 1,417
  • 1
  • 14
  • 27
krish
  • 2,498
  • 19
  • 25
49

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;
hakiko
  • 4,737
  • 6
  • 49
  • 96
Ashish
  • 490
  • 5
  • 10
13

@Christian (I can't comment because my reputation is too low) 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

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'

UPDATE (again)*

I know this is kind of off topic but...

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

import { SafeAreaView } from 'react-navigation';
<SafeAreaView forceInset={{ bottom: 'never' }} />
jsonp
  • 380
  • 2
  • 10
  • 2
    Hi @jsonp unfortunately i didn't see your response but I solved it basically like you did. I wanted to thank you a little belated for your efforts, even when I was not able to read your message in time. So: thanks! (also upvoted) – Christian Jun 13 '18 at 11:05
  • Used this and it worked well, i was trying to add a View with set height under the flatlist component to no avail. This cleaned up my issue of the last list item being partially covered by my bottom nav. – IronWorkshop Jan 08 '20 at 21:27
  • jsonp By now you do have enough rep to comment. But let me mention that even if you quote the rule you are breaking, it still applies to you. So if this could not by chance be seen as a helpful answering contribution to the question itself, then it would have to be deleted. Please rephrase this into an answer contribution based on but noticably extending the answer which got commented on by Chritian (or that is how I understand what happened here). If you cannot do that and still believe that this should be a comment, then please turn it into a comment. (I would not. you know. So - try! :-) – Yunnosch Mar 13 '20 at 07:07
13

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.

Amit
  • 953
  • 10
  • 16
  • 2
    "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." .... very useful comment! Thanks. – Shailesh Appukuttan May 17 '20 at 15:32
12

use contentContainerStyle props of FlatList

<FlatList contentContainerStyle={{ paddingBottom: 20}} />
S.Mahdi
  • 129
  • 1
  • 6
10

You can try this solution

For Vertical FlatList:

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

For Horizontal FlatList:

<FlatList
  contentContainerStyle={{paddingRight:40}}
/>
jamal
  • 815
  • 5
  • 22
8

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.

Florin Dobre
  • 8,025
  • 2
  • 45
  • 68
1

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

0

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)
    }
}
Dhiraj Gupta
  • 7,159
  • 4
  • 43
  • 50
0

This worked for me.

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

Work very well for me

<FlatList
  data={data}
  contentContainerStyle={{ paddingBottom: 30 }}
  style={{height: '95%'}}
  renderItem={({ item, index }) => (
    <ListItem item={item} onPress={() => handlePress(item, index)} />
  )}
/>
kissu
  • 6,476
  • 5
  • 15
  • 33
0

This work very well in my case:

  <FlatList
    data={todos}
    contentContainerStyle={{ height: '100%' }}
    renderItem={({ item }) => <Todos items={item} pressed={pressed} />}
  />
Penny Liu
  • 7,720
  • 5
  • 40
  • 66
0

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