benevbright/react-navigation-collapsible

When used together with FlatList as per instruction, RefreshControl renders under the navigation header.

joonhocho opened this issue ยท 8 comments

Information

  • react-native version: 0.62.2 (latest)
  • react-navigation version: 5.5.0 (latest)
  • react-navigation-collapsible version: 5.6.1 (latest)
  • Platform (iOS/Android): iOS
  • react-native init or Expo: react-native init

Detail

When used together with FlatList as per instruction, RefreshControl renders under the navigation header.
Screen Shot 2020-06-02 at 2 52 57 AM

@joonhocho I have same problem. Did you find any solution?

There is progressViewOffset for FlatList but it's only for Android.
It seems to be a very old issue of RN. https://react-native.canny.io/feature-requests/p/offsetting-refreshcontrol-on-ios-via-progressviewoffset

I will get back to here if I find a workaround or a solution.

I encountered the same problem. Replacing the padding with contentInset only for iOS and creating a custom RefreshControl with progressViewOffset only for android worked on a standard FlatList.

However, I then ran into problems with my SectionList: the section header was also rendered with the contentInset. I did not manage to counter this and was not satisfied with all the platform specific exceptions in my components.

As an alternative solution, I applied the translateY animated value from the useCollapsibleStack hook to my SectionList as a whole. This in turn required me to apply the containerPaddingTop to the contentContainer bottom padding to prevent the list from getting cut off at the bottom.

  const {
    onScroll,
    scrollIndicatorInsetTop,
    containerPaddingTop,
    translateY,
  } = useCollapsibleStack()

  return (
    <Animated.SectionList
      // other props
      onScroll={onScroll}
      scrollIndicatorInsets={{ bottom: scrollIndicatorInsetTop }}
      contentContainerStyle={{ paddingBottom: containerPaddingTop }}
      scrollEventThrottle={16} // without this, it can get jittery.
      style={{
        transform: [
          {
            translateY: translateY.interpolate({
              inputRange: [0, containerPaddingTop],
              outputRange: [containerPaddingTop, 2 * containerPaddingTop],
            }),
          },
        ],
      }}
    />
  )

I encountered the same problem. Replacing the padding with contentInset only for iOS and creating a custom RefreshControl with progressViewOffset only for android worked on a standard FlatList.

However, I then ran into problems with my SectionList: the section header was also rendered with the contentInset. I did not manage to counter this and was not satisfied with all the platform specific exceptions in my components.

As an alternative solution, I applied the translateY animated value from the useCollapsibleStack hook to my SectionList as a whole. This in turn required me to apply the containerPaddingTop to the contentContainer bottom padding to prevent the list from getting cut off at the bottom.

  const {
    onScroll,
    scrollIndicatorInsetTop,
    containerPaddingTop,
    translateY,
  } = useCollapsibleStack()

  return (
    <Animated.SectionList
      // other props
      onScroll={onScroll}
      scrollIndicatorInsets={{ bottom: scrollIndicatorInsetTop }}
      contentContainerStyle={{ paddingBottom: containerPaddingTop }}
      scrollEventThrottle={16} // without this, it can get jittery.
      style={{
        transform: [
          {
            translateY: translateY.interpolate({
              inputRange: [0, containerPaddingTop],
              outputRange: [containerPaddingTop, 2 * containerPaddingTop],
            }),
          },
        ],
      }}
    />
  )

Android: It starts fluctuating / jitter screen while scrolling even different values of scrollEventtrottle is not solving it.

You're right! I'm not sure how I missed this on my initial tests but it indeed jitters quite a lot. It seems the android scroll gesture does not like us moving the list up and down during the scroll motion.

I did not find a good common solution so I ended up splitting my component into:

AnimatedSectionList.ios.tsx which uses the above transelateY hack.

AnimatedSectionList.android.tsx which uses the default collapsible header config and adds the progressViewOffset to a custom refreshControl as suggested by Bright.

Sorry about it.
FYI, the jittering issue is RN's very old bug. facebook/react-native#21801
If this bug was not present, this library would have had a lot more potential because it could have built in a much better way than using absolute position header. I only can tell you there are many limitations in this library.
I had tried to fix RN some time ago but only found out that the RN issue is not easy to fix due to its bad design and race conditions.

On Android, use progressViewOffset prop.

progressViewOffset={paddingHeight}               

On iOS, In the following props, use what you need.

contentInset={{ top: paddingHeight }}
contentContainerStyle={{ paddingTop: Platform.OS === 'ios' ? 0 : paddingHeight }}
contentOffset={{ y: -paddingHeight }}