satya164 / react-native-tab-view

A cross-platform Tab View component for React Native
MIT License
5.14k stars 1.07k forks source link

Flex style ignored when tab view inside ScrollView #1349

Closed paulsjohnson91 closed 1 year ago

paulsjohnson91 commented 2 years ago

Current behavior

expo demo When putting a tab view inside a ScrollView, the tabs contents don't show unless you put a static height value, it seems to ignore flex

Example

        <View style={{flex: 1}}>
            <ScrollView style={{flex: 1}}>
                <Text>Hello</Text>
                <TabView
                    navigationState={{index, routes}}
                    renderScene={renderScene}
                    onIndexChange={setIndex}
                    initialLayout={initialLayout}
                    style={{flex: 1}}
                />
            </ScrollView>
        </View>

With rendered scene

    <View style={{flex: 1}}>
        <Text>Hello</Text>
    </View>

If I were to set the tabview style to height: 900 then it renders (although obviously with blank space underneath) but if I leave it as flex: 1 then nothing renders

Expected behavior

Flex should make the tab contents render in the remaining space of the screen

Reproduction

https://snack.expo.dev/66VUyv9e2

Platform

Environment

"dependencies": { "expo": "~44.0.0", "expo-status-bar": "~1.2.0", "react": "17.0.1", "react-dom": "17.0.1", "react-native": "0.64.3", "react-native-web": "0.17.1", "react-native-tab-view": "3.1.1", "react-native-pager-view": "5.4.9" },

github-actions[bot] commented 2 years ago

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

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

paulsjohnson91 commented 2 years ago

Tested with versions listed above and results are the same

paulsjohnson91 commented 2 years ago

I've narrowed down what seems to be causing the problem for me:

In SceneView, the style object for the main view is

{
    "bottom": 0,
    "height": undefined,
    "left": 0,
    "position": "absolute",
    "right": 0,
    "top": 0,
    "width": undefined,
  },

and the main body is

        {
          focused || layout.width ? this.props.children({ loading }) : null
        }

The position being absolute here means the children don't render, and the || layout.width means that if the position is removed then the unfocused child is rendering in the background too, leaving blank space under the shorter children

DeveloperHarris commented 2 years ago

@paulsjohnson91 Thanks for investigating this. This issue seems to also be affecting the Material Top Tabs Navigator in React Navigation. Were you able to find a temporary solution?

paulsjohnson91 commented 2 years ago

@DeveloperHarris unforunately I've not had time to investigate in detail, for now I've just forked the repo and removed both the position: absolute and the || layout.width parts. This works fine for me as the above is my only use case for the tab view library however I haven't verified what would happen with other use cases yet. I will update if I can find a permanent solution

DeveloperHarris commented 2 years ago

@DeveloperHarris unforunately I've not had time to investigate in detail, for now I've just forked the repo and removed both the position: absolute and the || layout.width parts. This works fine for me as the above is my only use case for the tab view library however I haven't verified what would happen with other use cases yet. I will update if I can find a permanent solution

I found

{
     // Only render the route only if it's either focused or layout is available
     // When layout is not available, we must not render unfocused routes
     // so that the focused route can fill the screen
     focused || layout.width ? this.props.children({ loading }) : null
}

in SceneView.tsx, however, I wasn't able to find the style with the position:absolute or undefined height. @paulsjohnson91 do you remember what file those were located in?

Also, this occurs on android as well as ios, is it possible to update the tags?

github-actions[bot] commented 2 years ago

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

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

paulsjohnson91 commented 2 years ago

@DeveloperHarris

The position:absolute comes from the style object for the View in SceneView:

        style={[
          styles.route,
          // If we don't have the layout yet, make the focused screen fill the container
          // This avoids delay before we are able to render pages side by side
          layout.width
            ? { width: layout.width }
            : focused
            ? StyleSheet.absoluteFill
            : null,
          style,
        ]}

Specifically in the 'style' object above

I'm struggling to see where this style object is populated however I believe it comes from the types.tsx and is populated using the react-native Animated library

export type SceneRendererProps = {
  layout: Layout;
  position: Animated.AnimatedInterpolation;
  jumpTo: (key: string) => void;
};

I've now tested this with regular tab view not inside a tab view and removing the position option unfortunately doesn't work as it cuts off the bottom of the view.

It looks like we need position to be absolute when the tabview is not in a scrollview and to be undefined when it is.

I don't know if there's a better solution but maybe we could get a new TabView prop for nested

paulsjohnson91 commented 2 years ago

Just noticed the same issue occurs in this issue raised last year, which is loading a tab view into a modal

ajstokar commented 2 years ago

I'm running into this as well.

"react-native": "0.68.2", "@react-navigation/material-top-tabs": "^6.2.1", "react-native-tab-view": "^3.1.1",

MikaDeVries commented 2 years ago

Also running into this with react-navigation/material-top-tabs when trying to load it in a scrollview.

@react-navigation/material-top-tabs : "^6.2.1", "expo": "^45.0.0",

himrocks33 commented 2 years ago

Also, have an issue with this. Does anyone have a current work around? Is there a better version of this library to downgrade to that is working?

himrocks33 commented 2 years ago

I have a TabView not within a ScrollView, but each tab has it's own FlatList within it. First time I Load the FlatList are cut off at the bottom. If I navigate away and come back then the FlatList recalculate and it adjusts to the full height of the page. I'm assuming this is the same issue.

KeaganStevens commented 2 years ago

Downgrading to v2 seems to work for me.

KeaganStevens commented 2 years ago

The

I've narrowed down what seems to be causing the problem for me:

In SceneView, the style object for the main view is

{
    "bottom": 0,
    "height": undefined,
    "left": 0,
    "position": "absolute",
    "right": 0,
    "top": 0,
    "width": undefined,
  },

and the main body is

        {
          focused || layout.width ? this.props.children({ loading }) : null
        }

The position being absolute here means the children don't render, and the || layout.width means that if the position is removed then the unfocused child is rendering in the background too, leaving blank space under the shorter children

This style is from react-native-pager-view in utils.tsx

KeaganStevens commented 2 years ago

Changing layout.width ? { width: layout.width } : focused ? StyleSheet.absoluteFill : null, style

to

layout.width ? { width: layout.width } : focused ? StyleSheet.absoluteFill : null, {overflow:'visible'}

Solved it for me. It is not a proper fix though.

azcarraga commented 2 years ago

Try adding contentContainerStyle={{ flexGrow: 1 }} to the ScrollView:

<View style={{flex: 1}} >
    {/* START FIX */}
    <ScrollView style={styles.scene} contentContainerStyle={{ flexGrow: 1 }}>
    {/* END FIX */}
        <Text>Hello</Text>
        <TabView
            navigationState={{index, routes}}
            renderScene={renderScene}
            onIndexChange={setIndex}
            initialLayout={initialLayout}
            style={{flex: 1}}
        />
    </ScrollView>
</View>

Here's the modified snack above showing it working: https://snack.expo.dev/@aazcarraga/react-native-tab-view-nested-in-scroll-view

I spent a good amount of time myself trying to figure this one out, hope this helps!

karlerikjonatan commented 2 years ago

I was running into a similar problem myself, implementing TabView inside a SectionList.

I ended up using patch-package, overriding SceneView.

style={[
  styles.route,
  layout.width
    ? { width: layout.width }
    : focused
    ? StyleSheet.absoluteFill
    : null,
-   style,
+   style?.[0] 
// Only apply styles provided by sceneContainerStyle prop of TabView,
// ignoring absoluteFill from react-native-pager-view
]}

const styles = StyleSheet.create({
 route: {
   flex: 1,
-    overflow: 'hidden',
+    overflow: 'visible',
// Default to visible
 },
});

If absoluteFill is needed, I pass it through sceneContainerStyle with Stylesheet.absoluteFill.

mposborne commented 2 years ago

Try adding contentContainerStyle={{ flexGrow: 1 }} to the ScrollView:

<View style={{flex: 1}} >
    {/* START FIX */}
    <ScrollView style={styles.scene} contentContainerStyle={{ flexGrow: 1 }}>
    {/* END FIX */}
        <Text>Hello</Text>
        <TabView
            navigationState={{index, routes}}
            renderScene={renderScene}
            onIndexChange={setIndex}
            initialLayout={initialLayout}
            style={{flex: 1}}
        />
    </ScrollView>
</View>

Here's the modified snack above showing it working: https://snack.expo.dev/@aazcarraga/react-native-tab-view-nested-in-scroll-view

I spent a good amount of time myself trying to figure this one out, hope this helps!

Thank you! This worked for me.

arisyo13 commented 2 years ago

Try adding contentContainerStyle={{ flexGrow: 1 }} to the ScrollView:

<View style={{flex: 1}} >
    {/* START FIX */}
    <ScrollView style={styles.scene} contentContainerStyle={{ flexGrow: 1 }}>
    {/* END FIX */}
        <Text>Hello</Text>
        <TabView
            navigationState={{index, routes}}
            renderScene={renderScene}
            onIndexChange={setIndex}
            initialLayout={initialLayout}
            style={{flex: 1}}
        />
    </ScrollView>
</View>

Here's the modified snack above showing it working: https://snack.expo.dev/@aazcarraga/react-native-tab-view-nested-in-scroll-view

I spent a good amount of time myself trying to figure this one out, hope this helps!

If you try to run it for iOS and Android you will see that this solution is still not working and it works only for Web

szymonrybczak commented 2 years ago

@arisyo13 Can you please send some demo of that, not working for iOS or Android? The @azcarraga's solution with adding contentContainerStyle={{ flexGrow: 1 }} to <ScrollView /> seems to work.

okwasniewski commented 2 years ago

Closing this, since adding contentContainerStyle={{ flexGrow: 1 }} solves the issue. Thanks @mposborne 👍🏻

amireds commented 2 years ago

Try adding contentContainerStyle={{ flexGrow: 1 }} to the ScrollView:

<View style={{flex: 1}} >
    {/* START FIX */}
    <ScrollView style={styles.scene} contentContainerStyle={{ flexGrow: 1 }}>
    {/* END FIX */}
        <Text>Hello</Text>
        <TabView
            navigationState={{index, routes}}
            renderScene={renderScene}
            onIndexChange={setIndex}
            initialLayout={initialLayout}
            style={{flex: 1}}
        />
    </ScrollView>
</View>

Here's the modified snack above showing it working: https://snack.expo.dev/@aazcarraga/react-native-tab-view-nested-in-scroll-view

I spent a good amount of time myself trying to figure this one out, hope this helps!

This is not working on android and IOS. Try scrolling on either device, you'll see it doesn't scroll - Better still, add an element below the scrollview.

mtourj commented 2 years ago

As @amireds said, this does not work in native.

kunalkumar007 commented 2 years ago

Does anyone got it working till now ?

maximilize commented 1 year ago

@okwasniewski This issue is not resolved. Under most conditions, there are still the same problems.

okwasniewski commented 1 year ago

@maximilize Let's reopen this. I will take a look at this one more time when I have time

thanhdevapp commented 1 year ago

the same

cam-shaw commented 1 year ago

@okwasniewski just pinging to gauge if you think you'll have any time to look at this one again soon 🙏

quicksilverr commented 1 year ago

@satya164 @okwasniewski Please could you look into this issue? Scrollview is not working when wrapped under tab view?

thraxxdv commented 1 year ago

Same issue but implemented using @react-navigation/material-top-tabs

I have a ScrollView with a cover photo and the bottom of that are tabs:

<ScrollView>
     <CoverPhoto/>
     <Tabs />
</ScrollView>
iainmck commented 1 year ago

Seeing this in @react-navigation/material-top-tabs as well when I place the Tab view inside a SafeAreaView. You can see the components colored here, which demonstrates the improper size of the tab container.

I have a satisfactory workaround which is overflow: 'visible', though a solution that sets proper height would be great.

<Tab.Navigator 
  style={{flex: 1, backgroundColor: 'red'}}
  sceneContainerStyle={{backgroundColor: 'blue', flex: 1, overflow: 'visible'}}
>
    <Tab.Screen name="Posts 🎨" component={PostsTab} />
    ...

IMG_2A9DFB1A04F4-1

danielaloycedaniel commented 1 year ago

I was running into a similar problem myself, implementing TabView inside a SectionList.

I ended up using patch-package, overriding SceneView.

style={[
  styles.route,
  layout.width
    ? { width: layout.width }
    : focused
    ? StyleSheet.absoluteFill
    : null,
-   style,
+   style?.[0] 
// Only apply styles provided by sceneContainerStyle prop of TabView,
// ignoring absoluteFill from react-native-pager-view
]}

const styles = StyleSheet.create({
 route: {
   flex: 1,
-    overflow: 'hidden',
+    overflow: 'visible',
// Default to visible
 },
});

If absoluteFill is needed, I pass it through sceneContainerStyle with Stylesheet.absoluteFill.

This worked to me but I left overflow hidden as it was. I only changed style to style?.[0]

vlkpa commented 1 year ago

Same with nested ScrollViews

<ScrollView>
     <CoverPhoto/>
     <ScrollView>
         <Tabs />
      </ScrollView>
</ScrollView>

Same issue but implemented using @react-navigation/material-top-tabs

I have a ScrollView with a cover photo and the bottom of that are tabs:

<ScrollView>
     <CoverPhoto/>
     <Tabs />
</ScrollView>
okwasniewski commented 1 year ago

I think what you are trying to achieve can be done using this library.

Unfortunately at the moment we are not planning to support this. It's not possible to measure height of content inside of ScrollView (without some hacky workarounds), you can achieve it with this library by setting an explicit height.

Also since we are trying to make this library easy to maintain (and contribute to) this does not correlate with the main idea to display simple tab view. Therefore I'm closing all issues regarding embedding tab-view inside of ScrollView.

dylmye commented 1 year ago

Therefore I'm closing all issues regarding embedding tab-view inside of ScrollView.

If this common usecase is no longer supported, should the README be updated with the above information?

okwasniewski commented 1 year ago

If this common usecase is no longer supported, should the README be updated with the above information?

@dylmye The information about ScrollView not being supported, have been there the whole time. You can check it here: https://github.com/satya164/react-native-tab-view#avoid-rendering-tabview-inside-scrollview

dylmye commented 1 year ago

@okwasniewski "will disable the optimizations" is different from "doesn't show at all" though :)