Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using react-native-tab-view breaks when inside of a ScrollView #11067

Closed
5 of 14 tasks
alexdobson opened this issue Dec 2, 2022 · 38 comments
Closed
5 of 14 tasks

Using react-native-tab-view breaks when inside of a ScrollView #11067

alexdobson opened this issue Dec 2, 2022 · 38 comments

Comments

@alexdobson
Copy link

alexdobson commented Dec 2, 2022

Current behavior

Placing a Tab.Navigator inside of a ScrollView results in the height of the Tab.Screen route elements to disappear. This issue is not specific to use of @react-navigation/​material-top-tabs, it's an issue with react-native-tab-view itself.

I've added a reproducible expo snack example here. Note that this is not an issue on Web, only on Android and iOS (which is the most likely use case of this functionality.

<ScrollView>
  <View style={{ paddingVertical: 60 }}>
    <Text>My Header</Text>
  </View>
  <Tab.Navigator>
    <Tab.Screen name="Home" component={HomeScreen} />
    <Tab.Screen name="Settings" component={SettingsScreen} />
  </Tab.Navigator>
</ScrollView>

If you set the contentContainerStyle={{ flexGrow: 1 }} on the ScrollView then it does display the Tab.Screen route, however it doesn't allow the ScrollView to be scrollable, it simply allows the Tab.Screen route content to fill any remaining space in the ScrollView:

<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
  <View style={{ paddingVertical: 60 }}>
    <Text>My Header</Text>
  </View>
  <Tab.Navigator>
    <Tab.Screen name="Home" component={HomeScreen} />
    <Tab.Screen name="Settings" component={SettingsScreen} />
  </Tab.Navigator>
</ScrollView>

This seems to be an issue that people have run into a lot, which makes sense as it's such a common use-case. Here are a few of the similar issues:

Expected behavior

The Tab.Navigator content should expand to the height of it's content and allow the ScrollView to be scrollable:

175271218-089d27f8-78b0-4382-a1ce-c8ee86ffe563
(This image was posted by @IzaELK in this issue)

Reproduction

https://snack.expo.dev/@alexdobsontevent/react-native-tab-view-nested-in-scroll-view

Platform

  • Android
  • iOS
  • Web
  • Windows
  • MacOS

Packages

  • @react-navigation/​bottom-tabs
  • @react-navigation/​drawer
  • @react-navigation/​material-bottom-tabs
  • @react-navigation/​material-top-tabs
  • @react-navigation/​stack
  • @react-navigation/​native-stack
  • react-native-tab-view
  • flipper-plugin-react-navigation

Environment

  • I've removed the packages that I don't use
package version
@react-navigation/native 6.0.16
@react-navigation/material-top-tabs 6.4.0
@react-navigation/native-stack 6.9.2
react-native-safe-area-context 4.4.1
react-native-screens 3.18.2
react-native-gesture-handler 2.8.0
react-native-reanimated 2.12.0
react-native-tab-view 3.3.2
react-native-pager-view 6.0.1
react-native 0.70.5
expo 47.0.8
node 18.12.0
npm or yarn 1.22.19
@github-actions
Copy link

github-actions bot commented Dec 2, 2022

Couldn't find version numbers for the following packages in the issue:

  • flipper-plugin-react-navigation
  • @react-navigation/bottom-tabs
  • @react-navigation/drawer
  • @react-navigation/material-bottom-tabs
  • @react-navigation/stack

Can you update the issue to include version numbers for those packages? The version numbers must match the format 1.2.3.

The versions mentioned in the issue for the following packages differ from the latest versions on npm:

  • @react-navigation/native (found: 6.0.14, latest: 6.0.16)

Can you verify that the issue still exists after upgrading to the latest versions of these packages?

@github-actions
Copy link

github-actions bot commented Dec 2, 2022

Couldn't find version numbers for the following packages in the issue:

  • flipper-plugin-react-navigation
  • @react-navigation/bottom-tabs
  • @react-navigation/drawer
  • @react-navigation/material-bottom-tabs
  • @react-navigation/stack

Can you update the issue to include version numbers for those packages? The version numbers must match the format 1.2.3.

@github-actions
Copy link

github-actions bot commented Dec 2, 2022

Couldn't find version numbers for the following packages in the issue:

  • flipper-plugin-react-navigation

Can you update the issue to include version numbers for those packages? The version numbers must match the format 1.2.3.

@pavsamurai
Copy link

Having the same issue

@Josh2941
Copy link

Same issue. The Scene inside the tabs becomes blank. Reproducible snack -> https://snack.expo.dev/U2sRWWDvr

@DazEdword
Copy link

Encountered the same issue, almost every dependency equal to those of the original issue opener.

@Marcados
Copy link

Marcados commented Jan 4, 2023

Same issue. This is a very common use-case, it should be fixed as a matter of priority.

@DannyBiezen
Copy link

Running into the same issue. This is a major blocker for us. It would be great to have this fixed as it seems very common functionality

@zispidd
Copy link

zispidd commented Jan 11, 2023

Same issue

@kokoa0402
Copy link

Same issue!

@xhirazi
Copy link

xhirazi commented Jan 19, 2023

+1

@xhirazi
Copy link

xhirazi commented Jan 19, 2023

Had to downgrade to v2.16

@okwasniewski
Copy link
Contributor

Hey,

As stated here: satya164/react-native-tab-view#1349 (comment) tab view won't support embedding inside scroll view. Closing this

@okwasniewski okwasniewski closed this as not planned Won't fix, can't repro, duplicate, stale Jan 20, 2023
@Kailash8799
Copy link

same issue

@github-actions
Copy link

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

@Yousef-Hegazy
Copy link

Yousef-Hegazy commented Jan 3, 2024

This is not a perfect fix if you're not sure about the height of the content of your screens, and it may feel hacky, but it is one of the few solutions to this problem. I just wanted to share it here:
I ended up wrapping the Tab.Navigator in a View with minHeight style, also I added a style of flex: 1 to the parent ScrollView and the View wrapper around each of the tabs, just to make them take the full space.

<ScrollView style={{flex: 1}}>
  <View style={{paddingVertical: 60}}>
    <Text>My Header</Text>
  </View>
  <View style={{minHeight: 500}}>
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  </View>
</ScrollView>
const HomeScreen = () => (
  <View style={{flex: 1}}>
    {/* Home screen content */}
  </View>
);

Note: If you're unsure about the height of home screen and think that you may need scrolling you can also wrap the HomeScreen content in a ScrollView instead of View like this and enable nested scrolling with nestedScrollEnabled:

const HomeScreen = () => (
  <ScrollView nestedScrollEnabled style={{flex: 1}}>
    {/* Home screen content */}
  </ScrollView>
);

@apatheticL
Copy link

apatheticL commented Jan 5, 2024

@Yousef-Hegazy Thanks for idea but I tried your way it not working right, in View of TabBarView only show with heigh is 500 but not show full in home screen when i scroll down in screen

  • Home screen i has a Flash List view all item like
  <View style={[styles.container]}>
     <FlatList
       numColumns={3}
       nestedScrollEnabled
       scrollEnabled={false}
       data={DataProduct}
       renderItem={({item}) => {
         return <ItemProductComponent item={item} onItemPress={itemPress} />;
       }}
     />
   </View>

container: {
marginTop: pxToPercentage(10),
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},

@Yousef-Hegazy
Copy link

Yousef-Hegazy commented Jan 5, 2024

@apatheticL Sorry, I don't understand what you mean, and I can't find any Tab in the provided code. Besides, I said it worked for me in this use case, so it might not be a good solution for you as it is just adding a fixed height to the container of Tab.Navigator component. Try to provide more code so that I or anyone else can help.

@emanuel-maker
Copy link

Same issue. 👎

@apatheticL
Copy link

apatheticL commented Jan 8, 2024

@Yousef-Hegazy sorry, I try code of you into my project and it not work right for me
My Screen code:

 <ScrollView
        style={{flex: 1}}
        contentContainerStyle={{flexGrow: 1}}>
          <BannerComponent images={ImageBanner} />
          <FlashSaleTopComponent
               onItemPress={onItemPress}
               onShowAll={onFastSalePress}
          />
         <SaleTopComponent onShowAll={onTimageSale} onItemPress={onItemPress} />
         <View style={{height: 500}}>
             <TopNavigation />
         </View>
 </ScrollView>

In TopNavigation :

<Tab.Navigator
      screenOptions={{
        headerShown: false,
      }}>
      <Tab.Screen name={'Tab'} component={HomeScreen} />
</Tab.Navigator>

in Home screen

  <View style={[styles.container]}>
     <FlatList
       numColumns={3}
       nestedScrollEnabled
       scrollEnabled={false}
       data={DataProduct}
       renderItem={({item}) => {
         return <ItemProductComponent item={item} onItemPress={itemPress} />;
       }}
     />
  </View>

I tried your suggest set style view tag is flex: 1 but it does not show and only shows when i set height is number. Thanks so much.

@Yousef-Hegazy
Copy link

Yousef-Hegazy commented Jan 8, 2024

@apatheticL I'm glad it works for you now, and if you don't mind using a different library, then I think there is a suggested library for this kind of use case by PedroBern Here. However, I haven't tried it before.

@apatheticL
Copy link

@Yousef-Hegazy Thank you very much, the PedroBern library seems suitable to me for this case.

@alexstock1
Copy link

@okwasniewski

Hey,

As stated here: satya164/react-native-tab-view#1349 (comment) tab view won't support embedding inside scroll view. Closing this

Why did you close the issue if it hasn't been resolved? If you're not qualified and don't have enough knowledge to fix it, it's clear you should reopen it to allow someone from the community to address it.

From your side, it appears highly unprofessional and portrays you as a developer who may not be adequately qualified and unwilling to support open-source solutions.

To me, it sounds like you're admitting you can't fix it because you don't know how which is truly unfortunate.

Please reopen the issue and allow someone else to address it. At least then, we can keep track of its progress.

@lucasb243
Copy link

HI everyone, got the same issue. Happy about any updates on this.

@IvanNasonov
Copy link

If anyone is still having this problem, upgrading react-native-pager-view to version 7.0.0-rc.1 seems to have solved the problem for me

@alexstock1
Copy link

@IvanNasonov could you share some code examples if you can, please?

@lucasb243
Copy link

Hey, meanwhile I switched to react-native-collapsible-tab-view, this is not the ideal Solution But works for me. Hope this helps.

@IvanNasonov
Copy link

IvanNasonov commented Feb 9, 2024

A little bit of an update - event though switching to version 7.0.0-rc.1 helped with the screen not being able to scroll at all, some other issues arose, the most severe of which is that now some screens in the material tab navigator seemingly randomly stop responding to touch. I'm hoping this issue will not reproduce on real devices (I've testing on a simulator), but it probably will, so switching to react-native-collapsible-tab-view will probably be the option I will go for too, but I will keep you updated and provide code examples later during the week

@IvanNasonov
Copy link

IvanNasonov commented Feb 11, 2024

So our testers checked it out and it looks like the issue I described above does reproduce on real devices, so it looks like we will be going with react-native-collapsible-tab-view since there seem to be no other option other than some kind of custom solution. For anybody curious I created a repo with scrollable tabs working, you can find the main code here

@lucasb243
Copy link

Thanks for sharing, is anyone able to remove the dropshadow below the Tabbar? Really struggling with that, appreciate the input.

@rsuubinn
Copy link

공유해 주셔서 감사합니다. 탭바 아래의 그림자를 제거할 수 있는 사람이 있나요? 정말 어려움을 겪고 있습니다. 의견을 보내 주셔서 감사합니다.

tabBarStyle {
   elevation: 0,
}

@YoucefBen47
Copy link

A little bit of an update - event though switching to version 7.0.0-rc.1 helped with the screen not being able to scroll at all, some other issues arose, the most severe of which is that now some screens in the material tab navigator seemingly randomly stop responding to touch. I'm hoping this issue will not reproduce on real devices (I've testing on a simulator), but it probably will, so switching to react-native-collapsible-tab-view will probably be the option I will go for too, but I will keep you updated and provide code examples later during the week

Hi @IvanNasonov we tried react-native-collapsible-tab-view but it's the same issue. We have a component then under it there is a tab view and everything is inside a ScrollView. Unless we add a minHeight to the containerStyle of the Tab Container nothing is rendered !!

chrisbobbe added a commit to chrisbobbe/zulip-mobile that referenced this issue Apr 15, 2024
When we upgrade @react-navigation/material-top-tabs soon, it will no
longer support putting tabs inside a ScrollView:
  react-navigation/react-navigation#11067 (comment)

So, wrap the content of each tab, instead of wrapping the whole tab
navigator, taking care to copy the ScrollView's effective
configuration.
@FazliddinFayziev
Copy link

FazliddinFayziev commented Jun 22, 2024

To be Honest, I cound not fix this problem by using any library, then decided to write own tabBar code. Here is my code, feel free to use it and enjoy!

Tabs:

import { dynamicStyles } from './Styles';
import { Button } from 'react-native-paper';
import React, { ReactNode, useState } from 'react';
import { branchColor } from '../../assets/colors/colors';
import { useGlobalContext } from '../../context/context';
import { StyleProp, View, ViewStyle } from "react-native";

interface Child {
    name: string;
    component: ReactNode;
}

interface ChildrenProp {
    sx?: StyleProp<ViewStyle>;
    tabActiveColor?: string;
    tabNonActiveColor?: string; 
    children: React.ReactElement<Child>[];
}

const Tabs: React.FC<ChildrenProp> = ({ children, sx, tabActiveColor, tabNonActiveColor }) => {

    // Global
    const { mode } = useGlobalContext();
    const styles = dynamicStyles(mode);

    // Local
    const [active, setActive] = useState(children[0].props.name);

    // Render Component
    const renderComponent = (active: string) => {
        const child = children.find((child) => child.props.name === active);
        return child ? child.props.component : null
    }

    return (
        <>
            <View style={styles.tabsButtonsContainer}>
                {children.map((child, index:number) => {
                    const { name } = child.props
                    const curActive = active === name ? "contained" : "outlined"
                    const buttonColor = active === name ? 
                        (tabActiveColor ? tabActiveColor : branchColor) : 
                        (tabNonActiveColor ? tabNonActiveColor : "transparent")
                    return (
                        <Button 
                            key={index} 
                            mode={curActive}
                            style={[styles.tab, sx]} 
                            buttonColor={buttonColor}
                            onPress={() => setActive(name)}
                        >
                            {name}
                        </Button>
                    )
                })}
            </View>
            {renderComponent(active)}
        </>
    )
}

export default Tabs

Tab:

import React, { ReactNode } from 'react'

interface Child {
    name: string;
    component: ReactNode;
}

const Tab = ({ name, component }:Child) => {
    return null;
}

export default Tab

Styles:

import { StyleSheet } from "react-native";
import { branchColor } from "../../assets/colors/colors";
import { changeMode } from "../../assets/colors/modeFunction";

export const dynamicStyles = (mode:boolean) => {

    const {  } = changeMode(mode);

    return StyleSheet.create({
        tabsButtonsContainer: {
            flexDirection: "row",
            gap: 10,
            justifyContent: "flex-start",
            marginLeft: 20,
            marginTop: 10
        },

        tab: {
            borderColor: branchColor,
        },
    })
}

Example of use case:

<Tabs>
     <Tab name='Posts' component={<ProfilePosts />} />
     <Tab name='Comments' component={<ProfileComments />} />
</Tabs>

It works for both iOS and Android! Feel free to customize anything! If you cannot understand, please ask. Try to use it, no need for a library. I have checked LinkedIn; they are also doing the same thing. The main difference between a library and our own code is that, in a library, your tabs will really navigate between pages, but in our own code, they will not. Try to click on any tab and click on the back button; it will not go to the previous tab, but in the use case of a library, it will.

@sersadaka
Copy link

@FazliddinFayziev where is the assests folder and others like branchcolor, can you please share a complete folder having all these folders

@FazliddinFayziev
Copy link

@sersadaka I will send it you soon. But you can use your own custom colors and assests. I am using branchColor and other colors, because it is requirement of company.

@wolfcarves
Copy link

To be Honest, I cound not fix this problem by using any library, then decided to write own tabBar code. Here is my code, feel free to use it and enjoy!

Tabs:

import { dynamicStyles } from './Styles';
import { Button } from 'react-native-paper';
import React, { ReactNode, useState } from 'react';
import { branchColor } from '../../assets/colors/colors';
import { useGlobalContext } from '../../context/context';
import { StyleProp, View, ViewStyle } from "react-native";

interface Child {
    name: string;
    component: ReactNode;
}

interface ChildrenProp {
    sx?: StyleProp<ViewStyle>;
    tabActiveColor?: string;
    tabNonActiveColor?: string; 
    children: React.ReactElement<Child>[];
}

const Tabs: React.FC<ChildrenProp> = ({ children, sx, tabActiveColor, tabNonActiveColor }) => {

    // Global
    const { mode } = useGlobalContext();
    const styles = dynamicStyles(mode);

    // Local
    const [active, setActive] = useState(children[0].props.name);

    // Render Component
    const renderComponent = (active: string) => {
        const child = children.find((child) => child.props.name === active);
        return child ? child.props.component : null
    }

    return (
        <>
            <View style={styles.tabsButtonsContainer}>
                {children.map((child, index:number) => {
                    const { name } = child.props
                    const curActive = active === name ? "contained" : "outlined"
                    const buttonColor = active === name ? 
                        (tabActiveColor ? tabActiveColor : branchColor) : 
                        (tabNonActiveColor ? tabNonActiveColor : "transparent")
                    return (
                        <Button 
                            key={index} 
                            mode={curActive}
                            style={[styles.tab, sx]} 
                            buttonColor={buttonColor}
                            onPress={() => setActive(name)}
                        >
                            {name}
                        </Button>
                    )
                })}
            </View>
            {renderComponent(active)}
        </>
    )
}

export default Tabs

Tab:

import React, { ReactNode } from 'react'

interface Child {
    name: string;
    component: ReactNode;
}

const Tab = ({ name, component }:Child) => {
    return null;
}

export default Tab

Styles:

import { StyleSheet } from "react-native";
import { branchColor } from "../../assets/colors/colors";
import { changeMode } from "../../assets/colors/modeFunction";

export const dynamicStyles = (mode:boolean) => {

    const {  } = changeMode(mode);

    return StyleSheet.create({
        tabsButtonsContainer: {
            flexDirection: "row",
            gap: 10,
            justifyContent: "flex-start",
            marginLeft: 20,
            marginTop: 10
        },

        tab: {
            borderColor: branchColor,
        },
    })
}

Example of use case:

<Tabs>
     <Tab name='Posts' component={<ProfilePosts />} />
     <Tab name='Comments' component={<ProfileComments />} />
</Tabs>

It works for both iOS and Android! Feel free to customize anything! If you cannot understand, please ask. Try to use it, no need for a library. I have checked LinkedIn; they are also doing the same thing. The main difference between a library and our own code is that, in a library, your tabs will really navigate between pages, but in our own code, they will not. Try to click on any tab and click on the back button; it will not go to the previous tab, but in the use case of a library, it will.

Doesn't satisfy the problem, when adding stickyHeaderIndices the entire tabs goes with the scroll instead with the TabView only.

@FazliddinFayziev
Copy link

Doesn't satisfy the problem, when adding stickyHeaderIndices the entire tabs goes with the scroll instead with the TabView only.

Man, If it does not satify you and does not solve your problem, here is my email: fazrez4515@gmail.com
Just type me your problem, even we can have a zoom so that I can help you to solve your problem!
But this code in above has to work!

@TimothyLuedtke
Copy link

Safe to say this is still an issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests