facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.21k stars 24.33k forks source link

[Android] ScrollView clips children even with overflow: 'visible' #31218

Open RickardZrinski opened 3 years ago

RickardZrinski commented 3 years ago

Description

Child elements of ScrollView are clipped when positioned outside of the ScrollView bounds, even when the style and contentContainerStyle props have been given overflow: 'visible'.

React Native version:

System: OS: macOS 10.15.7 CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz Memory: 538.14 MB / 16.00 GB Shell: 5.7.1 - /bin/zsh Binaries: Node: 12.18.0 - ~/.nvm/versions/node/v12.18.0/bin/node Yarn: Not Found npm: 6.14.4 - ~/.nvm/versions/node/v12.18.0/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.10.1 - /Users/rickard/.rbenv/shims/pod SDKs: iOS SDK: Platforms: iOS 14.2, DriverKit 20.0, macOS 11.0, tvOS 14.2, watchOS 7.1 Android SDK: API Levels: 28, 29, 30 Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.0, 30.0.2 System Images: android-30 | Google APIs Intel x86 Atom, android-30 | Google Play Intel x86 Atom Android NDK: Not Found IDEs: Android Studio: 4.0 AI-193.6911.18.40.6514223 Xcode: 12.2/12B45b - /usr/bin/xcodebuild Languages: Java: 11.0.7 - /Users/rickard/.jenv/shims/javac Python: 3.9.1 - /Users/rickard/.pyenv/shims/python npmPackages: @react-native-community/cli: Not Found react: 16.13.1 => 16.13.1 react-native: 0.63.3 => 0.63.3 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Expected Results

Given that overflow: visible is set on both the style and contentContainerStyle props on the ScrollView the overflowing children of the ScrollView should be visible.

Snack, code example, screenshot, or link to a repository:

https://snack.expo.io/Xw1c66W8R

mattijsf commented 3 years ago

I'm seeing the same using react-native 0.64. A bit strange because according to the changelog it should have been fixed in 0.57.2: https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#v0572

mattijsf commented 3 years ago

Not ideal, for the time being I'm using this workaround to have a scrollable overflowing container:

<FlatList
  data={undefined}
  renderItem={undefined}
  ListHeaderComponent={
    <View style={styles.overflowing}>
      ...
    </View>
  }
/>
RickardZrinski commented 2 years ago

Overriding the getClipChildren method to return false in ReactHorizontalScrollView seems to do the trick.

@Override
public boolean getClipChildren() {
  return false;
}
Before After

Here's the JSX:

<ScrollView
  horizontal={true}
  style={{overflow: 'visible'}}
  contentContainerStyle={{
    backgroundColor: 'yellow',
  }}>
  <Pressable
    onPress={() => Alert.alert('Pressed')}
    style={{
      backgroundColor: 'blue',
      height: 100,
      width: 75,
      marginTop: -50,
    }}
  />
  <Text
    style={{
      marginTop: -15,
      backgroundColor: 'transparent',
    }}>
    A very very very very very very very very very very very very very
    very very very very very very long text
  </Text>
</ScrollView>

However I don't see any other usages of getClipChildren in the RN codebase which makes me suspect it's not the right way to solve it.

The ReactViewGroup class does make a call to setClipChildren with a false argument. This was introduced in https://github.com/facebook/react-native/commit/4af4da9089e20aa84bc5660bfb37763556442a4e, which should've solved overflows for ScrollView(s).

public ReactViewGroup(Context context) {
  super(context);
  setClipChildren(false);
}

However, ReactHorizontalScrollView doesn't extend ReactViewGroup.

escwald commented 2 years ago

There is a problem with ScrollView (plus all components inheriting from it) and overflow style prop, when using the RefreshControl on Android. Using React Native v0.67.3 as of writing this post.

Looking into the ScrollView.js of the react-native module you can find this code:

if (refreshControl) {
      if (Platform.OS === 'ios') {
        // ...
      } else if (Platform.OS === 'android') {
        // On Android wrap the ScrollView with a AndroidSwipeRefreshLayout.
        // Since the ScrollView is wrapped add the style props to the
        // AndroidSwipeRefreshLayout and use flex: 1 for the ScrollView.
        // Note: we should split props.style on the inner and outer props
        // however, the ScrollView still needs the baseStyle to be scrollable
        const {outer, inner} = splitLayoutProps(flattenStyle(props.style));

        return React.cloneElement(
          refreshControl,
          {style: StyleSheet.compose(baseStyle, outer)},
          <NativeDirectionalScrollView
            {...props}
            style={StyleSheet.compose(baseStyle, inner)}
            ref={this._setNativeRef}>
            {contentContainer}
          </NativeDirectionalScrollView>,
        );
      }
    }

Which has two problems.

  1. It splits the overflow style prop only to the inner style, resulting in that the outer component does not override overflow and uses its default value scroll. This could be solved by not splitting overflow and letting both inner and outer get that style prop.

  2. It wraps the ScrollView with AndroidSwipeRefreshLayout, which does not seem to support overflow: 'visible', even if you fix problem 1.

Attaching a overflow-test.zip, which shows two FlatLists using overflow: 'visible' next to each other and with an empty space above them. The list to the left (red items) is using RefreshControl and the one to the right (blue items) is not.

Android: Screenshot 2022-03-28 at 12 26 14

iOS: Screenshot 2022-03-28 at 12 32 09

liviu-padurariu commented 2 years ago

Hello, did someone managed to find a fix for this?

sungsong88 commented 2 years ago

Any news on this issue?

nofacez commented 2 years ago

Also facing same issue šŸ˜”

sxyshirly commented 2 years ago

Also

kneza23 commented 1 year ago

same thing :(

19BCS1114 commented 1 year ago

@kneza23 @sxyshirly @nofacez

If you are using overflow property inside a scroll view or flatlist, add this prop to your scrollview or flatlist

removeClippedSubviews(false)

On Android the default value is true which obstructs it from rendering extra content.

gmferraz commented 1 year ago

Same issue :(

lgibso34 commented 1 year ago

@kneza23 @sxyshirly @nofacez

If you are using overflow property inside a scroll view or flatlist, add this prop to your scrollview or flatlist

removeClippedSubviews(false)

On Android the default value is true which obstructs it from rendering extra content.

@19BCS1114 Your solution does not seem to work for the Author's snack: https://snack.expo.io/Xw1c66W8R

Just add the prop removeClippedSubviews={false} to the ScrollView and you'll see it does not change the UI at all. I checked on an Android device too

MCElmo commented 1 year ago

having this issue aswell

Andrerm124 commented 11 months ago

Anything ever come from this? 3 years later still trying to find a workaround to this very problem šŸ¤”

feng-yu-healthbank commented 8 months ago

The same issue is still there. removeClippedSubviews is now false by default and setting it false doesn't do anything.

bdtren commented 8 months ago

In my case, the issue was because I set width: screenWidth in contentContainerStyle and when I remove contentContainerStyle, and add a view inside with that style, it working.

.....
        <ScrollView
          // contentContainerStyle={styles.scrollViewContent} //=> remove this
        >
          <View style={styles.scrollViewContent}> {/*add this*/}
          {/*some contents*/}
          </View>
        </ScrollView>

.....
const styles = StyleSheet.create({
  scrollViewContent: {
    flexGrow: 1,
    width: screenWidth,
    overflow: 'visible',
  },

});
WayneKim92 commented 2 months ago

I had a similar issue.

If you are using KeyboardAvoidingView, I recommend checking that this component has no padding or margin. remove padding and margin value!