createMaterialTopTabNavigator inside ScrollView with dynamic height - react-native

Is there any way to create something like a ScrollView with dynamic height?
Details on what we're trying to do:
We created a Top Tab Bar (using createMaterialTopTabNavigator) within a ScrollView. Everything works fine, except the height of the ScrollView. Let's assume there are 3 tab screens with different heights: TabScreen1: 800, TabScreen2: 400, TabScreen3: 300... At rendering, the ScrollView takes the greatest height, and when Tab2 or 3 is selected, the height of our ScrollView remains at 800, so there is empty space for scrolling in Tab2 and 3.

As I promised, here is how we solved it.
In your class where you have your ScrollView you need a state like
const [activeTab, setActiveTab] = useState('tab1') //your default (first) tab
Later, you want to change that state every time you change tab:
<TabsStack.Screen
name={'tab1'}
component={
activeTab === 'tab1' ? Tab1Screen : DefaultScreen
}
listeners={{ focus: () => setActiveTab('tab1') }}
/>
Now every time a tab is unselected or rather not focused it will show DefaultScreen which is an empty view and has no height like:
const DefaultScreen = () => (
<Box
flex={1}
py={20}
alignItems="center"
justifyContent="center"
bg="background"
>
<ActivityIndicator size="large" color="white" animating />
</Box>
)
In my case (see question above), every time I switch from Tab1.1.1 to Tab1.1.2, Tab1.1.1 will change to DefaultScreen

Try something like this,
Fix the height of the parent widget to the maximum screen height you want that Tab Navigator to have.
<ListView or ScrollView>
<View style={{height: (width)/(0.8)}} >
<Tab.Navigator tabBar={(props) => <TabBar {...props} />}>
<Tab.Screen name="T1" component={T1} />
<Tab.Screen name="T2" component={T2} />
<Tab.Screen name="T3" component={T3} />
</Tab.Navigator>
</View>
</ ListView or ScrollView>
And for tabs do Something like this
T1 ->
<View style={styles.container}>
<ScrollView nestedScrollEnabled={true}>
<FlatList
numColumns={3}
data={allMedia}
keyExtractor={(item) => item.id}
listKey={(post) => `${post.id}D`}
renderItem={({ item }) => (Anything)}
scrollEnabled ={false}
/>
</ScrollView>
</View>
Remember to disable the scroll view Inside the FlatList of tabs and wrap with a ScrollView with nestedScrollEnabled={true}

Use this contentContainerStyle={{flex: 1}} inside scroll view
<ScrollView contentContainerStyle={{flex: 1}}>
...
// your tab
</ScrollView>

There is no need to put TabNavigator inside ScrollView. Put ScrollView inside of each TabScreen instead
Answering your question: if your ScrollView is the same height as the content then it doesn't scroll. You are probably talking about height of a contentContainer of a ScrollView. And the only wayto set it dynamically is to use onLayout to measure content height of currently selected tab, save this number to state and apply it as a height of in contentContainerStyle prop of a ScrollView. I don't recommend hacking it this way unless absolutely necessary though

Related

How to show Flatlist item separator component on bottom

I have some items in a flatList in which I have used itemSeparator component to show the divider between the list but by default flatList render divider in between items, and I need to show the divider to the bottom also, is that any way to do this.
Thanks in advance
I have tried with index but that is not working on the flatList item separator, and I do not want to customise item to show the bottom line of the item. so is it possible with item separator to show the bottom line.
You can wrap your items to CellRendererComponent https://reactnative.dev/docs/virtualizedlist#cellrenderercomponent and apply styling to it
Try just put divider in renderItem function. With this you will have clear YourItem and Divider in any element you have, or you can try use Example #2.
<View style={{flex:1}}>
<FlatList
data={DATA}
renderItem={({item}) => <><YourItem {...item} /><Divider/></>}
contentContainerStyle={{flexGrow:1}}
/>
</View>
Example #2
<View style={{flex:1}}>
<FlatList
data={DATA}
renderItem={renderYourItem}
ListFooterComponent={() => <><ItemSeparatorComponent/><CustomButton /></>}
contentContainerStyle={{flexGrow:1}}
/>
</View>

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation

I'm building a mobile application with React Native, and I have a ScrollView inside one of the pages.
The problem is that I have in this page a FlatList so when I go to that page, I get this error:
VirtualizedLists should never be nested inside plain ScrollViews with the same orientation... The thing is, I set the ScrollView nestedScrollEnabled to true and I also set the FlatList's scrollEnabled to false because I don't really need the scroll there (I'm using it to render items from an enum) so I thought that should fix the error, but the error still persist.
It looks like this:
<ScrollView showsVerticalScrollIndicator={false} nestedScrollEnabled={true}>
<MyComponent />
</ScrollView>
And MyComponent has this FlatList:
<FlatList
scrollEnabled={false}
style={{padding: 8,marginBottom: 8,}}
data={categories}
numColumns={numOfColumns}
keyExtractor={category => category}
renderItem={item => renderItem(item.item)}
/>
It essentially looks like this:
<SafeAreaView>
<ScrollView>
<FlatList />
<OTHER COMPONENTS />
</ScrollView>
</SafeAreaView>
Your component FlatList and ScrollView have the same orientation(vertical), so you need put your component inside a ScrollView with horizontal orientation like this:
<View>
<ScrollView nestedScrollEnabled={true} style={{ width: "100%" }} >
<View>
<ScrollView horizontal={true} style={{ width: "100%" }}>
<MyComponent />
</ScrollView>
</View>
</ScrollView>
</View>
The error is because you are using a FlatList inside a ScrollView.
The FlatList is already a scrollable component,
FlatList are accepting props like ListHeaderComponent for rendering header content and ListFooterComponent for rendering footer content. Here you can remove the ScrollView and instead of that you can only render FlatList for making whole page scrollable
You need to change your code as shown below:
const _renderHeader = () => <Text>Your Header</Text>
const _renderFooter = () => <Text>Your Footer</Text>
const _renderFooter = () => <Text>FlatList items</Text>
<SafeAreaView>
<FlatList
data={data}
keyExtractor={(item, index) => String(index)}
renderItem={_renderItem}
ListHeaderComponent={_renderHeader}
ListFooterComponent={_renderFooter}
ListFooterComponentStyle={styles.footerStyle} //If you need to give specific styles for footer component
ListHeaderComponentStyle={styles.headerStyle} //for header comp style
/>
</SafeAreaView>
You don't want to use a ScrollView when you are using a FlatList
You can check the official docs for more clarification

React Navigation 5 Top bar with FlatList Scrolling and Dynamic Height

I wanted to create a similar feature of how Instagram has on their profile page, where you're able to swipe on different tabs (your posts, and posts people tagged you in).
I've tried attempting to create it using React Navigation 5's Top Tab Navigator https://reactnavigation.org/docs/material-top-tab-navigator and FlatList, but not sure if its the correct way of doing it.
Profile Screen Component
const ProfileScreen = () => (
<>
<FlatList
data={null}
ListHeaderComponent={
<View style={{ marginBottom: 20 }}>
// Added the component and navigator here because of scrolling capabilities
<UserInfo />
<Tab.Navigator>
<Tab.Screen name="Images" component={ImageScreen} />
<Tab.Screen name="TaggedImages" component={TaggedImageScreen} />
</Tab.Navigator>
</View>
}
/>
</>
)
Image Screen
const ImageScreen = () => (
<>
<FlatList
data={data}
renderItem={({ item }) => (
<View>
<Image src={item} />
</View>
)}
/>
</>
)
Tagged Image Screen
const TaggedImageScreen = () => (
<>
<FlatList
data={data}
renderItem={({ item }) => (
<View>
<Image src={item} />
</View>
)}
/>
</>
)
There's a couple questions/issues with this:
The height of the Tab Navigator is not dynamic to its respected component - meaning, if I have 3 images in Image Screen component, and 10 images in Tagged Screen component, the height will expand to the Tagged screen component (for both)
The reason for the FlatList in my Profile Component is because I want to be able to scroll the component (similar to Instagram's profile).
So this brings to the next question: Is having the Tab Navigator in the List Header Component of the FlatList the best method to doing something similar to IG? Is there another way I should handle this feature?

React Native Flatlist resets scroll position after initial hide and show. How to prevent?

I’m working on a React Native screen. It has two tabs. Let's just call it Tab 1 and Tab 2
Each tab will show a Flatlist with data specific to the tab.
I control the showing or hiding of each Flatlist.
Here's the weird behavior:
When the screen loads, Tab 1 is the default that shows.
Scroll Flatlist 1
Tap on Tab 2, Flatlist 2 shows
Tap on Tab 1, the scroll position is back to the beginning of the list!
After this initial weird behavior, any subsequent scrolling in either tab's flatlist MAINTAINS the scroll position of where you left it.
What can I do to fix this?
Here's the return from my render():
// Need to have two different components (and show/hide them accordingtly)
// so that they each maintain their own scroll position, etc.
const youActivityStyle = activityTabIndex === 0 ? { display: 'flex' } : {display: 'none'}
const followingActivityStyle = activityTabIndex === 0 ? { display: 'none'} : {display: 'flex' }
return (
<React.Fragment>
<View style={youActivityStyle}>
<FlatList
data={activityDataMe}
renderItem={this.renderItem}
keyExtractor={this.activityKeyExtractor}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
contentContainerStyle={styles.flatListContentContainerStyle}
onRefresh={this.onRefresh}
refreshing={activityRefreshing}
onEndReached={this.flatListOnEndReached}
onEndReachedThreshold={0.5}
onMomentumScrollBegin={this.flatListOnMomentumScrollBegin} />
</View>
<View style={followingActivityStyle}>
<FlatList
data={activityDataFollowing}
renderItem={this.renderItem}
keyExtractor={this.activityKeyExtractor}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
contentContainerStyle={styles.flatListContentContainerStyle}
onRefresh={this.onRefresh}
refreshing={activityRefreshing}
onEndReached={this.flatListOnEndReached}
onEndReachedThreshold={0.5}
onMomentumScrollBegin={this.flatListOnMomentumScrollBegin} />
</View>
</React.Fragment>
)

Flatlist covering the a element with absolute position

I was trying to make a view that I wanted to come over a flatlist so that on clicking it user can reach the top.But the flatlist was covering that element.
<View style={{backgroundColor:'#e6e6e6',flex:1,}>
<View style={{position:'absolute'}}>
<Text>Scroll To Reload</Text>
</View>
<FlatList refreshing={this.state.refresh} onRefresh={()=>this.refreshAllTweets()}
data={tweets}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) =>
<TweetItem onPress={()=>this.goToDetail(item.id)} onImagePress={()=>this.toggleModal(item.id)}
onCommentPress={()=>this.showComments(item.id)} tweet={item}/>}
/>
</View>
Each child in a view is rendered in order, and ultimately stacked on top of each other until all the children are rendered. As the view you want on the top is being rendered first it is going to be at the bottom. Change your code so that the view that you want on the top is rendered last. i.e. move it to the bottom of the view.
<View style={{backgroundColor:'#e6e6e6',flex:1}}>
<FlatList
refreshing={this.state.refresh}
onRefresh={()=>this.refreshAllTweets()}
data={tweets}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => <TweetItem onPress={()=>this.goToDetail(item.id)} onImagePress={()=>this.toggleModal(item.id)} onCommentPress={()=>this.showComments(item.id)} tweet={item}/>}
/>
<View style={{position:'absolute', top: 0}}>
<Text>Scroll To Reload</Text>
</View>
</View>
As the view is now absolutely positioned you may wish to give it an actual position.
Update for comment
Flatlist covering the a element with absolute position
FlatLast has a prop called ListFooterComponent it takes a React Component Class, a render function, or a rendered element. So you could update your FlatList by adding the following prop.
<FlatList
...
ListFooterComponent={
<View>
<Text>Scroll To Reload</Text>
</View>
}
/>
This will attach the view as a footer to the FlatList, so it will be visible when the bottom of your FlatList is reached.
Check out the docs for more information about FlatLists.