react-native: Flatlist not supported for RTL

<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

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 13
  • Comments: 77 (12 by maintainers)

Commits related to this issue

Most upvoted comments

This issue is fixed in RN 0.73.

adding inverted={true} just worked for me: <FlatList inverted={true} horizontal={true} />

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

30 years later : hey react native the issue still exits any luck , it has been open for 30 years now

2020 is a bad year, but may be this will get a fix?!

It’s 2020! any updates on this issue?

Having the same issue here, its open since 2018 …

I think we need to wait for another four years to have a fix 😂

2022 any solution for this?

There is another problem with scrollToItem! It works perfectly on iOS but it will get lost in android!

not fixed on RN 0.57

@matinzd Yes that’s right. What do you suggest?

I found this solution. scrollToIndex or scrollToItem work in this

<FlatList
		nestedScrollEnabled
		data={Calendar}
		keyExtractor={item => item.visit_day_identity}
		renderItem={renderItem}
		horizontal
		snapToEnd
		ref={ElementRef}
		style={{flexDirection: 'row-reverse'}}
	/>

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

This is still happening for us - FlatList with RTL on android simply jumps to second or third item instead of first item. We have 87 items in the list so it might precision issues with scrolling to the end.

This issue has changed for the worse in the latest version, this isn’t the same issue that you’re describing.

The bug you are describing : https://github.com/facebook/react-native/issues/34314

Right now there is no tricks to fix it with js anymore, also when you scroll a large list, it scrolls back to that weird position you mentioned.

This issue right now literally makes react native unusable for any app that supports rtl!!!

Please fix this issue it’s really anoying!

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?

2022 any solution for this?

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

But have anther issue

readmore issue

I only had to apply the patch https://github.com/Flipkart/recyclerlistview/pull/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.

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

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.

maybe this is better for show data in list in RTL layout:


<FlatList
	nestedScrollEnabled
	data={Calendar.reverse()}
	keyExtractor={item => item.visit_day_identity}
	renderItem={renderItem}
	horizontal
	pagingEnabled
	showsHorizontalScrollIndicator={false}
	snapToEnd
	ref={element}
	inverted={true}
/>

scrollToIndex or scrollToItem function will not properly work in this case.

maybe this is better for show data in list in RTL layout:


<FlatList
	nestedScrollEnabled
	data={Calendar.reverse()}
	keyExtractor={item => item.visit_day_identity}
	renderItem={renderItem}
	horizontal
	pagingEnabled
	showsHorizontalScrollIndicator={false}
	snapToEnd
	ref={element}
	inverted={true}
/>

Hey Guys, I am working in an Arabic company, and I encountered this issue, and solved this problem. Here is how I solved my problem and adopted FlatList into RTL.

Please read the comment lines, I tried to explain everything through comment lines.


/*
 Once this script is called, I need to scroll to a position in onMount state of the app, and even need to center it.
 If there was not a requirement for RTL then scrollToIndex was enough, but since this should be a RTL app, I needed to 
 make it via using offsets.
*/

const MyHorizontalRTLFriendlyProblematicFlatList: React.FC<Props> = props => {
	const { width } = useWindowDimensions();
	const listRef = useAnimatedRef<FlatList>();
	const listItemWidthRef = useRef<number[]>([]);
	const [didLayoutCalculationDone, setDidLayoutCalculationDone] = useState(false);

        // FlatList data comes from other page via routing
        const flatlistdata = props.navigationState.routes;

         // Those two parameter coming to this component from other page while routing.
	const currentIndex = props.navigationState.index;
	const currentItemKey = flatlistdata[currentIndex].key;

	useEffect(() => {
                // This one is simple as its name, I am repositioning once the Items are looped in the Flatlist and rendered.
               // Now it is time to scrollTo a specific offset.
		if (didLayoutCalculationDone) setTabItemPosition();
	}, [didLayoutCalculationDone]);

        // With this method, I am collecting every horizontal flatlist element's width into reference
       // I will need this once I needed to use scrollToOffset
	const onLayout = (index: number, event: LayoutChangeEvent): void => {
		listItemWidthRef.current[index] = event.nativeEvent.layout.width;

                 // since I need to reposition flatlist onMount, I need to know when the layout calculations are done!
                // So I am simply comparing item index with the source length. And updating the state didLayouCalculationDone
		if (index === flatlistdata.length - 1) setDidLayoutCalculationDone(true);
	};


        // This is where all magic is happen. scroll to an offset, end even make it centered
	const setTabItemPosition = () => {
                // First I compare the item's width against window width.
		const viewOffset = width / 2 - listItemWidthRef.current[currentIndex] / 2;
                const totalWidth = listItemWidthRef.current?.reduce((previousValue, currentValue) => previousValue + currentValue);
                // Second, I calculate the total width towards the Item that I want to scroll but not including the item's own width.
		const totalWidthUntilCurrentIndex = listItemWidthRef.current?.reduce(
			(previousValue, currentValue, i) => previousValue + (i < currentIndex ? currentValue : 0)
		);

                // There is one thing to note. RTL and LTR is working well on android even we use horizontal Flatlist and scrollToIndex
                // But since we use scrollToOffSet we need to make some adjustments for Android aswell.
		const calculatePosition =
			Platform.OS === "android" && I18nManager.isRTL
				? totalWidth - (totalWidthUntilCurrentIndex + listItemWidthRef.current[currentIndex]) - viewOffset
				: totalWidthUntilCurrentIndex - viewOffset;

                 // Lets Rock! Now I have all the informations for scrolling to a position
                // actually it is enogh to call only with totalWidthUntilCurrentIndex parameter but
                // since i need to reposition the item in the center of the screen, simply extract viewOffset.
                // Then everything works now. It even works on LTR, RTL, iOS, Android
		listRef.current?.scrollToOffset({ animated: true, offset: calculatePosition });
	};

	return (
		<FlatList
			ref={listRef}
			horizontal
			data={flatlistdata}
			showsHorizontalScrollIndicator={false}
			renderItem={({ item, index }: any) => (
                                 // by calling onLayout method, I am simply gathering every items own width and storing them into ref.
				<Pressable px="3" onLayout={event => onLayout(index, event)}>
                                     <Text>Hello My Problematic RTL Friendly FlatList</Text>
				</Pressable>
			)}
		/>
	);
};


If you are familiar and comfortable with those calculation parameters, I promise you once you get the logic, it will definitely work.

I’m facing the same issue and I cannot use disableVirtualization={true} because of performance issues it creates. Please fix this bug 😦(