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

`contentOffset` is not working on Android

Open mtflud opened this issue 1 year ago • 8 comments

Description

Passing contentOffset to any ScrollView on Android is not doing anything, looks like it's been broken since RN 0.67. Prior versions work as intended.

Version

0.68.2

Output of npx react-native info

System: OS: macOS 12.3.1 CPU: (8) arm64 Apple M1 Memory: 224.81 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 14.17.3 - ~/.nvm/versions/node/v14.17.3/bin/node Yarn: 1.22.11 - ~/.nvm/versions/node/v14.17.3/bin/yarn npm: 8.3.0 - ~/.nvm/versions/node/v14.17.3/bin/npm Watchman: 2022.02.21.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /Users/mtflud/.rbenv/shims/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5 Android SDK: API Levels: 23, 25, 27, 28, 29, 30, 31 Build Tools: 29.0.2, 29.0.3, 30.0.2, 30.0.3, 31.0.0 System Images: android-24 | ARM 64 v8a, android-30 | Google APIs ARM 64 v8a, android-30 | Google APIs Intel x86 Atom, android-30 | Google Play ARM 64 v8a Android NDK: 23.1.7779620 IDEs: Android Studio: 2021.2 AI-212.5712.43.2112.8609683 Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild Languages: Java: 15.0.1 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.68.2 => 0.68.2 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

  • Create any screen with a ScrollView or any component that inherits from it
  • Add a contentOffset={{y: 50}} prop to the ScrollView
  • Observe how iOS respects this offset, but Android does nothing at all

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

Given the following code:

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  useColorScheme,
  View,
} from 'react-native';

import {Colors} from 'react-native/Libraries/NewAppScreen';

const colors = ['red', 'blue', 'purple', 'green', 'black'];

const App: () => Node = () => {
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  return (
    <SafeAreaView style={backgroundStyle}>
      <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
      <ScrollView
        contentOffset={{y: 50}}
        contentInsetAdjustmentBehavior="automatic"
        style={backgroundStyle}>
        {Array.from({length: 5}).map((_, index) => (
          <View
            style={[styles.block, {backgroundColor: colors[index]}]}
            key={index}>
            {Array.from({length: 5}).map((_, index2) => (
              <View style={styles.line} key={`line_${index2}`}>
                <Text style={styles.text}>
                  Block {index} - Line {index2}
                </Text>
              </View>
            ))}
          </View>
        ))}
      </ScrollView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  block: {
    width: '100%',
  },
  line: {
    height: 50,
    width: '100%',
    borderBottomColor: 'white',
    borderBottomWidth: 1,
    justifyContent: 'center',
    paddingHorizontal: 15,
  },
  text: {
    color: 'white',
  },
});

export default App;

We can see that iOS is rendered initially from the desired offset (starting from Block 0 - Line 1), but Android is not.

Screen Shot 2022-06-10 at 8 55 21

mtflud avatar Jun 10 '22 13:06 mtflud

Hi all,

Any update about that ?

In another thread I found this workaround: https://github.com/facebook/react-native/issues/30533#issuecomment-1158649237

but with this solution, we see the scroll when its already rendered

We're waiting for a fix. thank you all

goriverna avatar Jun 28 '22 09:06 goriverna

You can use contentContainerStyle with paddingTop or paddingVertical.

Btw, for the kind of implementation, you are trying to do, is better if you use FlatList instead ScrollView

nicolascavallin avatar Jun 28 '22 13:06 nicolascavallin

Im having the same issue

Gabrielhanna07 avatar Jun 28 '22 21:06 Gabrielhanna07

Fixed on https://github.com/facebook/react-native/commit/be260b9f479a3b55ee43d2959d2c49fd3c1eb4ac

patch-package for 0.68.2 (Expo SDK 45 users):

Patch
diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java
index e9b9941..1afbb25 100644
--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java
+++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java
@@ -254,8 +254,8 @@ public class ReactScrollView extends ScrollView
   @Override
   protected void onLayout(boolean changed, int l, int t, int r, int b) {
     // Call with the present values in order to re-layout if necessary
-    // If a "pending" value has been set, we restore that value.
-    // That value gets cleared by reactScrollTo.
+     // If a "pending" content offset value has been set, we restore that value.
+    // Upon call to scrollTo, the "pending" values will be re-set.
     int scrollToX =
         pendingContentOffsetX != UNSET_CONTENT_OFFSET ? pendingContentOffsetX : getScrollX();
     int scrollToY =
@@ -954,23 +954,21 @@ public class ReactScrollView extends ScrollView
   }
 
   /**
-   * Calls `reactScrollTo` and updates state.
+   * Calls `updateFabricScrollState` and updates state.
    *
-   * <p>`reactScrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between
-   * scroll view and state. Calling raw `reactScrollTo` doesn't update state.
-   *
-   * <p>Note that while we can override scrollTo, we *cannot* override `smoothScrollTo` because it
-   * is final. See `reactSmoothScrollTo`.
+   * <p>`scrollTo` changes `contentOffset` and we need to keep `contentOffset` in sync between
+   * scroll view and state. Calling ScrollView's `scrollTo` doesn't update state.
    */
   @Override
   public void scrollTo(int x, int y) {
     super.scrollTo(x, y);
-    // The final scroll position might be different from (x, y). For example, we may need to scroll
-    // to the last item in the list, but that item cannot be move to the start position of the view.
-    final int actualX = getScrollX();
-    final int actualY = getScrollY();
-    ReactScrollViewHelper.updateFabricScrollState(this, actualX, actualY);
-    setPendingContentOffsets(actualX, actualY);
+    ReactScrollViewHelper.updateFabricScrollState(this);
+    setPendingContentOffsets(x, y);
+  }
+
+  private boolean isContentReady() {
+    View child = getChildAt(0);
+    return child != null && child.getWidth() != 0 && child.getHeight() != 0;
   }
 
   /**
@@ -981,8 +979,7 @@ public class ReactScrollView extends ScrollView
    * @param y
    */
   private void setPendingContentOffsets(int x, int y) {
-    View child = getChildAt(0);
-    if (child != null && child.getWidth() != 0 && child.getHeight() != 0) {
+    if (isContentReady()) {
       pendingContentOffsetX = UNSET_CONTENT_OFFSET;
       pendingContentOffsetY = UNSET_CONTENT_OFFSET;
     } else {

kaueDM avatar Jul 07 '22 19:07 kaueDM

same with RN version 0.66.4

fusandy avatar Nov 04 '22 03:11 fusandy

I still have this issue for RN version 0.70.4

Nikooos avatar Nov 09 '22 21:11 Nikooos

I still have this issue for RN version 0.68.7

HyoJeong1019 avatar Aug 11 '23 04:08 HyoJeong1019

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Feb 17 '24 05:02 github-actions[bot]

This issue was closed because it has been stalled for 7 days with no activity.

github-actions[bot] avatar Feb 24 '24 05:02 github-actions[bot]