Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flatlist not supported for RTL #19150

Closed
3 tasks done
DanGDroid opened this issue May 6, 2018 · 78 comments
Closed
3 tasks done

Flatlist not supported for RTL #19150

DanGDroid opened this issue May 6, 2018 · 78 comments
Labels
Bug Component: FlatList Help Wanted :octocat: Issues ideal for external contributors. Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project.

Comments

@DanGDroid
Copy link

<Flatlist> horizontal true component does not work properly on Rtl devices, it renders the items again and jumps to head of list. impossible to use

Environment

Environment:
OS: macOS High Sierra 10.13.4
Node: 8.2.1
Yarn: 1.3.2
npm: 5.5.1
Watchman: 4.7.0
Xcode: Xcode 9.3 Build version 9E145
Android Studio: 3.1 AI-173.4670197

Packages: (wanted => installed)
react: 16.3.2 => 16.3.2
react-native: 0.55.0 => 0.55.0

Steps to Reproduce

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  FlatList,
  I18nManager
} from 'react-native';

const getList = () => {
  let list = []
  for (i=0;i<100;i++)
    list.push({title:i,backgroundColor:getRandomColor()})
  return list
}

export default class AutoLinkDemo extends Component {
  constructor(props){
    super(props)
    this.state={
      data:getList()
    }
  }
  render() {
    return (
      <View style={styles.container}>
      <Text style={{fontWeight:'bold',fontSize:60}}>{`is RTL? ${I18nManager.isRTL}`}</Text>
      <View style={styles.list}>
        <FlatList 
          data={this.state.data}
          contentContainerStyle={{alignItems:'center', justifyContent:'center'}}
          renderItem={({ item }) => this.renderCardItem(item)}
          keyExtractor={(item, index) => index}
          horizontal
        />
      </View>
      </View>
    )
  }
  renderCardItem(item){
    return(
      <View style={[styles.cardItem,{backgroundColor:item.backgroundColor}]}>
        <Text style={{fontWeight:'bold',fontSize:40}}>{item.title}</Text>
      </View>
    )
  }
}

function getRandomColor() {
  var letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
    alignItems:'center', justifyContent:'center',
  },
  list: {
    height:250,
    backgroundColor: 'yellow',
    alignItems:'center', justifyContent:'center',
  },
  cardItem:{
    height:200,
    width:250,
    alignItems:'center',
    justifyContent:'center',
    margin:5,
  },
});

AppRegistry.registerComponent('AutoLinkDemo', () => AutoLinkDemo);

Expected Behavior

<Flat list should adopt to relevant direction as I18nManager.isRtl` returns

Actual Behavior

<Flatlist> horizontal true component does not work properly on Rtl devices, it renders the items again and jumps to head of list. impossible to use

if i add attribute legacyImplementation={true} it works fine but as said in documents i doent know what traits of Flatlist i loose.

rtl1
rtl

@stale

This comment has been minimized.

@stale stale bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 4, 2018
@DanGDroid

This comment has been minimized.

@stale stale bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 5, 2018
@DanGDroid
Copy link
Author

not fixed on RN 0.57

@tarouboy
Copy link

still experiencing this error (RN 0.55.4).
have to disable RTL if using horizontal flatlist / scrollview.

If the list data / content doesn't update, it works fine.
Otherwise everytime the inner content updated, the list jump back to the top,
and onEndReached event will keep firing as well.

Using legacyImplementation={true} helps. But you can't use scrollTo, onViewableItemsChanged or other interactive functions, because it will generate errors.

Can someone look into this issue, please?

@DanGDroid
Copy link
Author

@shergin @javache @hramos @sahrens
still not fixed,
can some one please handle this bug for the sake of millions users that use RTL languages

@hramos
Copy link
Contributor

hramos commented Jan 12, 2019

@DanGDroid let me know if there's a pull request we can take a look at.

@adirzoari

This comment has been minimized.

@DanGDroid
Copy link
Author

no solution but to do it your own, i implemented a lazy loader of ScrollView but does not have all the benefits of a virtualised list

@sahrens
Copy link
Contributor

sahrens commented Jan 16, 2019

What about disableVirtualization={true}? Don't have much time to look at this now - would love a PR :)

@adirzoari
Copy link

@sahrens I tried now, it's not working. it's still from left to right.

@hramos hramos added Component: FlatList Help Wanted :octocat: Issues ideal for external contributors. Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project. labels Jan 16, 2019
@hramos hramos removed the 🔶Lists label Jan 25, 2019
@hramos hramos removed the Bug Report label Feb 6, 2019
@FadiAboMsalam
Copy link

any update on this ?? still facing this only in RTL react-native version 0.57.3

@FadiAboMsalam
Copy link

is there any plan to fix the horizontal flatlist for RTL ?

@sahrens
Copy link
Contributor

sahrens commented Apr 9, 2019

We have an internal task tracking this: T42861459

@FadiAboMsalam
Copy link

@sahrens would please let me know what is the status of this internal task is there going to be any solution soon ?

@hramos
Copy link
Contributor

hramos commented May 20, 2019

While we are tracking this internally, this issue is still up for grabs. If anyone from the community is interested in providing a pull request, please go ahead.

@sammy-SC
Copy link
Contributor

Before this issue is resolved a quick workaround for now is to set initialNumToRender to number of items to be rendered.

@YazeedAsaad
Copy link

anyone found a solution to this yet ? as @sammy-SC said i tried initialNumToRender but it causes huge performance issues with a large list

@arasrezaei
Copy link

Hello there,
Just please use flashlist:
https://shopify.github.io/flash-list/docs/usage
And contribute to it, much much better and performant option :)

@Stevemoretz
Copy link

Hello there, Just please use flashlist: https://shopify.github.io/flash-list/docs/usage And contribute to it, much much better and performant option :)

Thank you for your kind suggestion however there are a few things wrong with it.

  1. Rtl is not okay itself in flashlist RTL Bug Android Shopify/flash-list#544
  2. Even if they fix that there are many libraries using the flatlist internally.

This issue is old and the problem described here doesn't happen anymore a new problem occurs on the 0.68 or sdk 45 for expo

#34314

Maybe it's better to close this and move there? @DanGDroid

@Stevemoretz
Copy link

We have an internal task tracking this: T42861459

And could you please also inform us about the state of that task and if you have considered the new issue? #34314

@matinzd
Copy link
Contributor

matinzd commented Aug 2, 2022

We have aninternal task tracking this: T42861459

Any updates on this internal issue? @cortinico

These kind of RTL issues in react native cause too much problem developing RTL applications and I have dealt with it for many years.

Some issues I have faced:

  • We cannot set RTL layout on demand and JS bundle needs to be restarted (2 times most of the time due to race condition between native and old arch js)
  • There are some workarounds for first issue like writing custom view component and invert everything but that will not solve the problem.
  • FlatList and in general virtualized list have too many problems while RTL is enabled
  • Some animated stuffs will not work porperly and need workarounds to make them compatible with RTL mode.

@Stevemoretz
Copy link

Stevemoretz commented Aug 2, 2022

These are all true but most of them are fixable with workaround the recent bug on the latest version though has no workarounds that's a terrible bug have you noticed it too?

On 0.68, just on start it will have a wrong scroll position and when you scroll far away it gets back that initial wrong position, this is like a major issue with no workarounds right now.

Btw:
That use of inversion for rtl, at first sounds like a good idea but then you get through a lot of problems with third party libraries, it's a pretty huge disadvantage.

@matinzd
Copy link
Contributor

matinzd commented Aug 2, 2022

These are all true but most of them are fixable with workaround the recent bug on the latest version though has no workarounds that's a terrible bug have you noticed it too?

On 0.68, just on start it will have a wrong scroll position and when you scroll far away it gets back that initial wrong position, this is like a major issue with no workarounds right now.

I have been experiencing this since I started working with rn 6 years ago 😅

@Stevemoretz
Copy link

These are all true but most of them are fixable with workaround the recent bug on the latest version though has no workarounds that's a terrible bug have you noticed it too?
On 0.68, just on start it will have a wrong scroll position and when you scroll far away it gets back that initial wrong position, this is like a major issue with no workarounds right now.

I have been experiencing this since I started working with rn 6 years ago 😅

On expo sdk 44, everything was almost fine with flatlist there was one slight issue only which could be fixed via a workaround.

The issue I'm mentioning happened quite recently though.

@noway
Copy link

noway commented Aug 2, 2022

I'm currently working on switching <FlatList to https://github.com/Flipkart/recyclerlistview whilst using Flipkart/recyclerlistview#629 as the RTL patch. Will report back if it's viable.

@noway
Copy link

noway commented Aug 3, 2022

Hi guys, recyclerlistview is working great for me - using it with

isHorizontal={true}
pagingEnabled={true}

and with RTL language (Hebrew) on Android, and not having any issues.

Cheers.

@Stevemoretz
Copy link

Stevemoretz commented Aug 4, 2022

Hi guys, recyclerlistview is working great for me - using it with

isHorizontal={true}
pagingEnabled={true}

and with RTL language (Hebrew) on Android, and not having any issues.

Cheers.

Thank you for reporting back, so you only applied that patch?

Isn't it more convenient for you to use flashlist from Shopify it works with recyclereview internally but provides a syntax same as FlatList.

That would make the migration of existing apps also easier.

I applied the patch of that PR Shopify/flash-list#544 (comment) and all yet couldn't get it working yet, didn't use the pagingEnabled though was that mandatory?

@noway
Copy link

noway commented Aug 5, 2022

I only had to apply the patch Flipkart/recyclerlistview#629 on the latest version of recyclerlistview (4.1.1 at the moment)

pagingEnabled is not mandatory - we just use pages in our design.

I considered flash-list but it has fewer GitHub stars than recyclerlistview.

@zanyar3
Copy link

zanyar3 commented Sep 9, 2022

2022 any solution for this?

add inverted={true} to FaltList.
Work for me

But have anther issue

readmore issue

@demedos
Copy link

demedos commented Oct 3, 2022

Hi guys, recyclerlistview is working great for me - using it with

isHorizontal={true}
pagingEnabled={true}

and with RTL language (Hebrew) on Android, and not having any issues.
Cheers.

Thank you for reporting back, so you only applied that patch?

Isn't it more convenient for you to use flashlist from Shopify it works with recyclereview internally but provides a syntax same as FlatList.

That would make the migration of existing apps also easier.

I applied the patch of that PR Shopify/flash-list#544 (comment) and all yet couldn't get it working yet, didn't use the pagingEnabled though was that mandatory?

FlashList has some other issues with RTL, see Shopify/flash-list#544 and Shopify/flash-list#620. Recyclerlistview with this patch Flipkart/recyclerlistview#629 is the best solution at the moment

@dev-arson
Copy link

Hello, I created solution for this bug, not perfect but almost working.


  import React, { useEffect, useRef, useState } from 'react'; 
  import { FlatList, I18nManager, Platform as DevicePlatform } from 'react-native';
  import { useFocusEffect, useNavigation } from '@react-navigation/native';
  import { Platform } from 'enums';

export default function UiFlatlist(props) {
  const ref = useRef();

  const {
    columnWrapperStyle = {},
    contentContainerStyle = {},
    data = [],
    getItemLayoutForScrollIndex,
    horizontal = false,
    initialNumToRender = 10,
    initialScrollIndex = 0,
    keyExtractor = ({ id }) => id,
    maxToRenderPerBatch = 1,
    numColumns = 1,
    onScroll = () => {},
    pagingEnabled = false,
    removeClippedSubviews = DevicePlatform.OS !== Platform.IOS, // for my own logic
    renderItem = () => {},
    scrollIndicatorInsets,
    setRef,
    inverted,
    showsHorizontalScrollIndicator = false,
    updateCellsBatchingPeriod = 100,
    // Note: 10 viewports above, 10 below, and one in between
    windowSize = 21,
  } = props;

  const overrideScroll = DevicePlatform.OS === Platform.ANDROID && inverted;

  const navigation = useNavigation();
  const [scrollPosition, setScrollPosition] = useState(0);
  const [canScrollToStart, setCanScrollToStart] = useState(true);
  const [wasRunned, setWasRunned] = useState(false);

  if (overrideScrolls) {
    useFocusEffect(() => {
      if (!wasRunned) {
        if (ref && ref.current) {
          setWasRunned(true);
          setTimeout(() => {
            ref.current.scrollToOffset({
              offset: scrollPosition
              });
            setCanScrollToStart(false);
          }, 100);
        }
      }
    });

    useEffect(() => {
      navigation.addListener('blur', () => {
        setCanScrollToStart(true);
        setWasRunned(false);
      });
    }, [navigation]);
  }

  useEffect(() => {
    if (setRef) {
      setRef(ref);
    }
  }, [ref]);

  const handleOnScroll = (e) => {
    setScrollPosition(e.nativeEvent.contentOffset.x);
    onScroll(e);
  };

  return (
    <FlatList
      columnWrapperStyle={numColumns > 1 ? columnWrapperStyle : null}
      contentContainerStyle={{
        ...contentContainerStyle,
        ...(overrideScrolls ? { flexDirection: 'row-reverse' } : {}),
      }}
      data={data}
      getItemLayout={getItemLayoutForScrollIndex}
      horizontal={horizontal}
      initialNumToRender={initialNumToRender}
      initialScrollIndex={initialScrollIndex}
      keyExtractor={keyExtractor}
      maxToRenderPerBatch={maxToRenderPerBatch}
      numColumns={numColumns}
      onScrollToIndexFailed={() => {}}
      onScroll={handleOnScroll}
      pagingEnabled={pagingEnabled}
      ref={ref}
      inverted={overrideScrolls}
      removeClippedSubviews={removeClippedSubviews}
      renderItem={renderItem}
      scrollIndicatorInsets={scrollIndicatorInsets}
      showsHorizontalScrollIndicator={showsHorizontalScrollIndicator}
      updateCellsBatchingPeriod={updateCellsBatchingPeriod}
      windowSize={windowSize}
    />
  );
}

This code allow to use horizontal list in RTL app , but when we have paggination enabled after scroll and new page was added then list jump to last itemIndex % maxToRenderPerBatch

Code is not perfect, after 5 years of inactivity from Facebook side I think
better that than nothing :)

@Stevemoretz
Copy link

Hello, I created solution for this bug, not perfect but almost working.


  import React, { useEffect, useRef, useState } from 'react'; 
  import { FlatList, I18nManager, Platform as DevicePlatform } from 'react-native';
  import { useFocusEffect, useNavigation } from '@react-navigation/native';
  import { Platform } from 'enums';

export default function UiFlatlist(props) {
  const ref = useRef();

  const {
    columnWrapperStyle = {},
    contentContainerStyle = {},
    data = [],
    getItemLayoutForScrollIndex,
    horizontal = false,
    initialNumToRender = 10,
    initialScrollIndex = 0,
    keyExtractor = ({ id }) => id,
    maxToRenderPerBatch = 1,
    numColumns = 1,
    onScroll = () => {},
    pagingEnabled = false,
    removeClippedSubviews = DevicePlatform.OS !== Platform.IOS, // for my own logic
    renderItem = () => {},
    scrollIndicatorInsets,
    setRef,
    inverted,
    showsHorizontalScrollIndicator = false,
    updateCellsBatchingPeriod = 100,
    // Note: 10 viewports above, 10 below, and one in between
    windowSize = 21,
  } = props;

  const overrideScroll = DevicePlatform.OS === Platform.ANDROID && inverted;

  const navigation = useNavigation();
  const [scrollPosition, setScrollPosition] = useState(0);
  const [canScrollToStart, setCanScrollToStart] = useState(true);
  const [wasRunned, setWasRunned] = useState(false);

  if (overrideScrolls) {
    useFocusEffect(() => {
      if (!wasRunned) {
        if (ref && ref.current) {
          setWasRunned(true);
          setTimeout(() => {
            ref.current.scrollToOffset({
              offset: scrollPosition
              });
            setCanScrollToStart(false);
          }, 100);
        }
      }
    });

    useEffect(() => {
      navigation.addListener('blur', () => {
        setCanScrollToStart(true);
        setWasRunned(false);
      });
    }, [navigation]);
  }

  useEffect(() => {
    if (setRef) {
      setRef(ref);
    }
  }, [ref]);

  const handleOnScroll = (e) => {
    setScrollPosition(e.nativeEvent.contentOffset.x);
    onScroll(e);
  };

  return (
    <FlatList
      columnWrapperStyle={numColumns > 1 ? columnWrapperStyle : null}
      contentContainerStyle={{
        ...contentContainerStyle,
        ...(overrideScrolls ? { flexDirection: 'row-reverse' } : {}),
      }}
      data={data}
      getItemLayout={getItemLayoutForScrollIndex}
      horizontal={horizontal}
      initialNumToRender={initialNumToRender}
      initialScrollIndex={initialScrollIndex}
      keyExtractor={keyExtractor}
      maxToRenderPerBatch={maxToRenderPerBatch}
      numColumns={numColumns}
      onScrollToIndexFailed={() => {}}
      onScroll={handleOnScroll}
      pagingEnabled={pagingEnabled}
      ref={ref}
      inverted={overrideScrolls}
      removeClippedSubviews={removeClippedSubviews}
      renderItem={renderItem}
      scrollIndicatorInsets={scrollIndicatorInsets}
      showsHorizontalScrollIndicator={showsHorizontalScrollIndicator}
      updateCellsBatchingPeriod={updateCellsBatchingPeriod}
      windowSize={windowSize}
    />
  );
}

This code allow to use horizontal list in RTL app , but when we have paggination enabled after scroll and new page was added then list jump to last itemIndex % maxToRenderPerBatch

Code is not perfect, after 5 years of inactivity from Facebook side I think better that than nothing :)

Thanks for sharing, seriously what is wrong with Facebook, it's not like they don't have rtl apps themselves do yourself a favor at least

@mohamedanwer123
Copy link

in 2023 ''m found good solution ----> move to flutter

@Stevemoretz
Copy link

I made a solution for this issue, hope it's working good with you.

Try this package https://www.npmjs.com/package/react-native-localized-flatlist-rtl-ltr

https://github.com/AHMED-5G/react-native-localized-flatlist-rtl-ltr

ltrggg ltrggg

Thank you finally something about this.
One question have you tried with more items try 50 instead of 5.

One huge problem with the current flatlist of react native is that when you scroll it scrolls back it starts at the middle also instead of beginning you scroll forward like go more than 10 items it suddenly scrolls back to the 5th items and so on.

Can you verify this problem doesn't exist with your package?

Also I checked you have android and ios folder in your github but in your readme's installation only mention yarn you probably forgot to mention it's not a Javascript library and needs more actions like pod install?

@AHMED-5G
Copy link

AHMED-5G commented May 7, 2023

Can you verify this problem doesn't exist with your package?

Also I checked you have android and ios folder in your github but in your readme's installation only mention yarn you probably forgot to mention it's not a Javascript library and needs more actions like pod install?

I'm sorry, my solution fixing margin when change language with LTR systems, so it's not solving these issues.

@dev-arson
Copy link

#37651
In rn-tester app this fix works

@KarthikBaleneni
Copy link

Issue Still Exists in React Native 0.70.10 , Any working solutions , Please let us know .
If RN team has fixed this issue in latest RN version ?

@mmujtabam
Copy link

Still open

@mohamed2m2018
Copy link

issue still exists

@NickGerleman
Copy link
Contributor

This issue is fixed in RN 0.73.

@AmrAyman-G
Copy link

Screenshot 2023-10-30 at 10 21 54 PM

This issue is still not fixed on android and RN 0.73 not released yet ?

@seyedmostafahasani
Copy link

I have released a new library where I attempted to address this issue. Please check it out.
You can see it in this link

@codefreak13
Copy link

I have released a new library where I attempted to address this issue. Please check it out. You can see it in this link

In your library, where did you implement the scroll ref?
FYI, this issue is about scroll ref on Android RTL not working properly

@codefreak13
Copy link

codefreak13 commented Dec 27, 2023

The solution for the Android RTL is to use scrollToIndex instead of scrollToOffset

const flatListRef = useRef<FlatList | null>(null);

   const scrollToSelectedItem = useCallback(
      (offset?: boolean) => {
         if (flatListRef.current) {
            const selectedIndex = list?.findIndex((item) => item.name === current);
            if (selectedIndex !== -1) {
               const totalWidthBeforeSelected = list
                  .slice?.(0, selectedIndex)
                  .reduce?.((acc, item) => acc + calculateChipButtonWidth(item), 0);
               if (offset) {
                  flatListRef.current.scrollToOffset({ offset: totalWidthBeforeSelected, animated: true });
               } else {
                  flatListRef.current.scrollToIndex({ index: selectedIndex, animated: true });
               }
            }
         }
      },
      [current, list, flatListRef.current]
   );

   useEffect(() => {
      scrollToSelectedItem();
   }, [scrollToSelectedItem]);
   
       <FlatList
         ref={(ref) => (flatListRef.current = ref)}
         data={list}
         horizontal
         showsHorizontalScrollIndicator={false}
         renderItem={renderItem}
         keyExtractor={(item) => item.name}
         onScrollToIndexFailed={() => scrollToSelectedFeed(true)}
         inverted={I18nManager.isRTL}
      />

@haipv01
Copy link

haipv01 commented Jul 12, 2024

I have released a new library where I attempted to address this issue. Please check it out. You can see it in this link

I see that you did custom RTL horizontal list view by ScrollView. It wrong in the began

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Component: FlatList Help Wanted :octocat: Issues ideal for external contributors. Issue: Author Provided Repro This issue can be reproduced in Snack or an attached project.
Projects
None yet
Development

No branches or pull requests