react-native-tab-view icon indicating copy to clipboard operation
react-native-tab-view copied to clipboard

Flex style ignored when tab view inside ScrollView

Open paulsjohnson91 opened this issue 2 years ago • 27 comments

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

  • [x] Android
  • [X] iOS
  • [ ] Web
  • [ ] Windows
  • [ ] MacOS

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" },

paulsjohnson91 avatar May 19 '22 10:05 paulsjohnson91

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

  • expo (found: 44.0.0, latest: 45.0.4)
  • react-native (found: 0.64.3, latest: 0.68.2)
  • react-native-pager-view (found: 5.4.9, latest: 5.4.17)

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

github-actions[bot] avatar May 19 '22 10:05 github-actions[bot]

Tested with versions listed above and results are the same

paulsjohnson91 avatar May 20 '22 11:05 paulsjohnson91

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

paulsjohnson91 avatar May 21 '22 06:05 paulsjohnson91

@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?

DeveloperHarris avatar Jun 01 '22 03:06 DeveloperHarris

@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

paulsjohnson91 avatar Jun 01 '22 22:06 paulsjohnson91

@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?

DeveloperHarris avatar Jun 05 '22 02:06 DeveloperHarris

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

  • expo (found: 44.0.0, latest: 45.0.5)
  • react-native (found: 0.64.3, latest: 0.68.2)
  • react-native-pager-view (found: 5.4.9, latest: 5.4.24)

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

github-actions[bot] avatar Jun 07 '22 16:06 github-actions[bot]

@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 avatar Jun 07 '22 17:06 paulsjohnson91

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

paulsjohnson91 avatar Jun 07 '22 20:06 paulsjohnson91

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",

ajstokar avatar Jun 12 '22 13:06 ajstokar

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",

MikaDeVries avatar Jun 13 '22 09:06 MikaDeVries

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 avatar Jun 15 '22 13:06 himrocks33

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.

himrocks33 avatar Jun 17 '22 13:06 himrocks33

Downgrading to v2 seems to work for me.

KeaganStevens avatar Jun 19 '22 08:06 KeaganStevens

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 avatar Jun 19 '22 08:06 KeaganStevens

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.

KeaganStevens avatar Jun 19 '22 10:06 KeaganStevens

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!

azcarraga avatar Jun 29 '22 12:06 azcarraga

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.

karlerikjonatan avatar Jul 08 '22 11:07 karlerikjonatan

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.

mposborne avatar Aug 04 '22 10:08 mposborne

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

arisyo13 avatar Aug 16 '22 08:08 arisyo13

@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.

szymonrybczak avatar Aug 30 '22 11:08 szymonrybczak

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

okwasniewski avatar Sep 01 '22 07:09 okwasniewski

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.

amireds avatar Sep 01 '22 23:09 amireds

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

mtourj avatar Sep 14 '22 21:09 mtourj

Does anyone got it working till now ?

kunalkumar007 avatar Sep 17 '22 05:09 kunalkumar007

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

maximilize avatar Sep 20 '22 09:09 maximilize

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

okwasniewski avatar Sep 20 '22 09:09 okwasniewski

the same

thanhdevapp avatar Sep 26 '22 14:09 thanhdevapp

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

cam-shaw avatar Oct 26 '22 02:10 cam-shaw

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

quicksilverr avatar Oct 27 '22 14:10 quicksilverr