software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
8.83k stars 1.29k forks source link

Layout animation doesn't work with FlatList #2737

Closed Pietro-Putelli closed 10 months ago

Pietro-Putelli commented 2 years ago

Reanimated version: 2.3.0 When I try to use FlatList instead of ScrollView, as shown in the tutorial below, the Layout.springify() doesn't work. https://docs.swmansion.com/react-native-reanimated/docs/tutorials/LayoutAnimations/layoutAnimations

Someone suggests to apply this path: https://patch-diff.githubusercontent.com/raw/piaskowyk/react-native/pull/1.patch. How can I apply the patch? Is there an alternative solution?

Thank you

github-actions[bot] commented 2 years ago

Issue validator

The issue is invalid!

DigitalZebra commented 2 years ago

I believe this is what itemLayoutAnimation is supposed to be for on Reanimated2's version of FlatList: https://github.com/software-mansion/react-native-reanimated/blob/main/src/reanimated2/component/FlatList.tsx#L26

So I'd expect something like this to work (instead of the Animated.ScrollView):

<Animated.FlatList
        data={participantList}
        keyExtractor={p => p.id}
        style={[{width: '100%'}]}
        itemLayoutAnimation={Layout.springify()}
        renderItem={({item: participant}) => {
          return (
            <Participant
              name={participant.name}
              onRemove={() => removeParticipant(participant.id)}
            />
          )
        }}
      />

However, I'm getting the same behavior as a normal FlatList- which is there's no springify on the position of the items when adding/removing.

andresribeiro commented 2 years ago

Same here

DigitalZebra commented 2 years ago

I believe this is what itemLayoutAnimation is supposed to be for on Reanimated2's version of FlatList: https://github.com/software-mansion/react-native-reanimated/blob/main/src/reanimated2/component/FlatList.tsx#L26

So I'd expect something like this to work (instead of the Animated.ScrollView):

<Animated.FlatList
        data={participantList}
        keyExtractor={p => p.id}
        style={[{width: '100%'}]}
        itemLayoutAnimation={Layout.springify()}
        renderItem={({item: participant}) => {
          return (
            <Participant
              name={participant.name}
              onRemove={() => removeParticipant(participant.id)}
            />
          )
        }}
      />

However, I'm getting the same behavior as a normal FlatList- which is there's no springify on the position of the items when adding/removing.

Ah turns out this code works on iOS, but not on Android - so seems like this is an Android only issue.

hirbod commented 2 years ago

Layout animation does not work on iOS either correctly. All the animations do only work as long as the Flatlist Item which you try to add or remove is not in the overflowed area. As soon as your element is in a overflowed (eg hidden segment), the animations do not trigger on them. When my list shrinks or I remove elements at an index where the item is not overflowed, the animation works. (at least on iOS)

Pietro-Putelli commented 2 years ago

I can now confirm, that react native reanimated 2.3.0 works with the code I’ve initially published. Thank you, all the software mansion developers for the best react-native library.

On 16 Jan 2022, at 9:26 PM, Hirbod @.***> wrote:

Layout animation does not work on iOS either correctly. All the animations do only work as long as the Flatlist Item which you try to add or remove is not in the overflowed area. As soon as your element is in a overflowed (eg hidden segment), the animations do not trigger on them. When my list shrinks or I remove elements at an index where the item is not overflowed, the animation works. (at least on iOS)

— Reply to this email directly, view it on GitHub https://github.com/software-mansion/react-native-reanimated/issues/2737#issuecomment-1013946285, or unsubscribe https://github.com/notifications/unsubscribe-auth/APL4WSMM5OHGLORW3ZOHIADUWMSYJANCNFSM5J6ISNKA. You are receiving this because you authored the thread.

j-piasecki commented 2 years ago

Hi! It's recommended to use FlatList exported from react-native-reanimated it should also solve the problem.

tarikko commented 2 years ago

Not working for android :( ig it's better to just use ScrollView instead :disappointed: anyway I will still wait for better support

000xuandu commented 2 years ago

Same bug.

"react-native-reanimated": "^2.4.1",

const DATA = Array(10)
  .fill(1)
  .map((_, index) => index + 1);

<Animated.FlatList
        data={data}
        style={{flex: 1}}
        renderItem={({item}) => (
          <Animated.View
            key={item}
            entering={FadeIn}
            exiting={FadeOut}
            layout={Layout.delay(100)}
            style={styles.listItem}>
            <Text>{item}</Text>
          </Animated.View>
        )}
      />

https://user-images.githubusercontent.com/33916400/157805089-cc2dc2f9-4122-4def-80ba-62e1b361bc8e.mov

arunim2405 commented 2 years ago

Any updates on this?

nandorojo commented 2 years ago

You need to use the itemLayoutAnimation prop on the Animated.FlatList:

// put this outside of render code
const layout = Layout.springify()

<Animated.FlatList 
  itemLayoutAnimation={layout}
/>
tarikko commented 2 years ago

You need to use the itemLayoutAnimation on the Animated.FlatList:

// put this outside of render code
const layout = Layout.springify()

<Animated.FlatList 
  itemLayoutAnimation={layout}
/>

If you can make a demo, then that would be awesome.

agoyal0422 commented 2 years ago

You need to use the itemLayoutAnimation prop on the Animated.FlatList:

// put this outside of render code
const layout = Layout.springify()

<Animated.FlatList 
  itemLayoutAnimation={layout}
/>

This doesn't work for me. I'm still getting no animations on a Flatlist View with numColumns={2}

kevindingens commented 2 years ago

Bumping this issue - problem persists across both iOS and Android, even when using the above suggested, elsewhere suggested, and combinations of each.

hadnet commented 1 year ago

This is still an issue, a very serious problem may add. I'm using Reanimated 3-rc and nothing works, I read like a dozen of issues and non of them fix this problem.

bi885895 commented 1 year ago

do you have any news? I code on my android device and when i delete item from array, animation don't work

eprice122 commented 1 year ago

I am seeing this issue as well

YounesKHENIFER commented 1 year ago

same here

milanp93 commented 1 year ago

You can make entering and exciting animations by wrapping your ListItem in Animated.View, or put it inside the ListItem component: `const renderItem = ({ item }) => (

)` Entering animation works for me on both Android and IOS, but the existing animations still not working. P.S. it works with classic FlatList don't need to import FlatList from Reanimated.
km5x2 commented 1 year ago

This issue still exists 😞

shedworth commented 1 year ago

Still a thing

amaurycoudr commented 1 year ago

get the same issue as well

fakhergh commented 1 year ago

You can make entering and exciting animations by wrapping your ListItem in Animated.View, or put it inside the ListItem component: const renderItem = ({ item }) => ( <Animated.View entering={BounceInLeft} exiting={BounceOutRight}> <PostItem item={item} /> </Animated.View> ) Entering animation works for me on both Android and IOS, but the existing animations still not working. P.S. it works with classic FlatList don't need to import FlatList from Reanimated.

Hi everyone, entering and exiting animations are not enough for list items animation's harmony and itemLayoutAnimation is not working with createAnimatedComponent function. Therefore, you should override the CellRendererComponent property of the animated list, see the example below for an animated flatlist, it is working fine on both Android and iOS.

import * as React from 'react';
import { FlatList } from 'react-native';

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

// you can use it to create a section list e.g: 
// const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);

function App() {
  const [items, setItems] = React.useState<Array<any>>([]);

  const renderItem = React.useCallback(
    ({ item }: any) => (
      <TouchableOpacity
          activeOpacity={0.9}
          onPress={() => {
            setItems(prevState => prevState.filter((i: number) => i !== item));
          }}>
          <View style={{height:100, marginVertical: 12, backgroundColor: "red" }}>
            <Text style={{ color: "white", fontSize: 18}}>
              {item}
            </Text>
          </View>
      </TouchableOpacity>
    ),
    [],
  );

  const onPress = React.useCallback(() => {
    setItems(prevState => {
      return [Date.now(), ...prevState];
    });
  }, []);

  const keyExtractor = React.useCallback((item: any) => {
    return item.toString();
  }, []);

  const renderCell = React.useCallback(
    (props: any) => (
      <Animated.View
         {...props}
          layout={Layout.springify()}
          entering={ZoomIn}
          exiting={ZoomOut}/>
    ),
    [],
  );

  return (
    <View style={{flex:1}} >
      <Button title="ADD ITEM" onPress={onPress} />
      <AnimatedFlatList
        contentContainerStyle={{ paddingHorizontal: 24 }}
        data={items}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        CellRendererComponent={renderCell}
      />
    </View>
  );
}

Here is visual results:

Android iOS
dimaportenko commented 1 year ago

@fakhergh thanks for sharing. I've just tried it with new expo project and

    "react-native-reanimated": "~2.14.4"

iOS is working just fine. Android isn't working. What version are you using?

NanddoSalas commented 1 year ago

@fakhergh I tried implementing CellRendererComponent while using reanimated: 2.14.4 but I didn't work on my project so I just copied and pasted your code and It didn't work again, can You share your package.json?

fukemy commented 1 year ago

any update? Same problem here https://snack.expo.dev/@fukemy/chat-reanimated-sample?platform=ios

fukemy commented 1 year ago

Hi, which version reanimated you used? This code work for ios, but not working with Android

dimaportenko commented 1 year ago

From my smoke test, it works on the version

"react-native-reanimated": "^3.1.0"
aurangs7 commented 1 year ago

Same issue with FlatList, no animation on Android or iOS.

fukemy commented 1 year ago

@dimaportenko can u share piecies of code that you make the animation work?

dimaportenko commented 1 year ago

@fukemy I think it was https://github.com/dimaportenko/react-native-reanimated-issues-2737. But I don't really remember.

Bonassa commented 1 year ago

You can make entering and exciting animations by wrapping your ListItem in Animated.View, or put it inside the ListItem component: const renderItem = ({ item }) => ( <Animated.View entering={BounceInLeft} exiting={BounceOutRight}> <PostItem item={item} /> </Animated.View> ) Entering animation works for me on both Android and IOS, but the existing animations still not working. P.S. it works with classic FlatList don't need to import FlatList from Reanimated.

Hi everyone, entering and exiting animations are not enough for list items animation's harmony and itemLayoutAnimation is not working with createAnimatedComponent function. Therefore, you should override the CellRendererComponent property of the animated list, see the example below for an animated flatlist, it is working fine on both Android and iOS.

import * as React from 'react';
import { FlatList } from 'react-native';

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

// you can use it to create a section list e.g: 
// const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);

function App() {
  const [items, setItems] = React.useState<Array<any>>([]);

  const renderItem = React.useCallback(
    ({ item }: any) => (
      <TouchableOpacity
          activeOpacity={0.9}
          onPress={() => {
            setItems(prevState => prevState.filter((i: number) => i !== item));
          }}>
          <View style={{height:100, marginVertical: 12, backgroundColor: "red" }}>
            <Text style={{ color: "white", fontSize: 18}}>
              {item}
            </Text>
          </View>
      </TouchableOpacity>
    ),
    [],
  );

  const onPress = React.useCallback(() => {
    setItems(prevState => {
      return [Date.now(), ...prevState];
    });
  }, []);

  const keyExtractor = React.useCallback((item: any) => {
    return item.toString();
  }, []);

  const renderCell = React.useCallback(
    (props: any) => (
      <Animated.View
         {...props}
          layout={Layout.springify()}
          entering={ZoomIn}
          exiting={ZoomOut}/>
    ),
    [],
  );

  return (
    <View style={{flex:1}} >
      <Button title="ADD ITEM" onPress={onPress} />
      <AnimatedFlatList
        contentContainerStyle={{ paddingHorizontal: 24 }}
        data={items}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        CellRendererComponent={renderCell}
      />
    </View>
  );
}

That worked for me. I just made a callback function for the CellRendererComponent, and for typescript is possible to type the AnimatedFlatList.

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList<FLATLIST_DATA_TYPE>);

const renderCell = useCallback(
    (props: any) => (
      <Animated.View
        {...props}
        entering={SlideInRight}
        exiting={SlideOutRight}
        layout={Layout.springify()}
      />
    ),
    []
  );

<AnimatedFlatList
  data={taskHistory}
  keyExtractor={item => item.id}
  renderItem={({ item }) => (
   // Normal use
  )}
  CellRendererComponent={renderCell} // <- Add this
  ItemSeparatorComponent={ListSeparator}
/>
marco2216 commented 1 year ago

Entering animation was not working for me in the renderCell on Android. But layout in renderCell works, and then combining that with adding an entering animation in the renderItem give the expected result. You don't need to make the FlatList an animated component for this to work by the way.

Edit: except it makes the last item disappear randomly when removing elements from the list on Android...

ali-soltanii commented 11 months ago

You can make entering and exciting animations by wrapping your ListItem in Animated.View, or put it inside the ListItem component: const renderItem = ({ item }) => ( <Animated.View entering={BounceInLeft} exiting={BounceOutRight}> <PostItem item={item} /> </Animated.View> ) Entering animation works for me on both Android and IOS, but the existing animations still not working. P.S. it works with classic FlatList don't need to import FlatList from Reanimated.

Hi everyone, entering and exiting animations are not enough for list items animation's harmony and itemLayoutAnimation is not working with createAnimatedComponent function. Therefore, you should override the CellRendererComponent property of the animated list, see the example below for an animated flatlist, it is working fine on both Android and iOS.

import * as React from 'react';
import { FlatList } from 'react-native';

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

// you can use it to create a section list e.g: 
// const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);

function App() {
  const [items, setItems] = React.useState<Array<any>>([]);

  const renderItem = React.useCallback(
    ({ item }: any) => (
      <TouchableOpacity
          activeOpacity={0.9}
          onPress={() => {
            setItems(prevState => prevState.filter((i: number) => i !== item));
          }}>
          <View style={{height:100, marginVertical: 12, backgroundColor: "red" }}>
            <Text style={{ color: "white", fontSize: 18}}>
              {item}
            </Text>
          </View>
      </TouchableOpacity>
    ),
    [],
  );

  const onPress = React.useCallback(() => {
    setItems(prevState => {
      return [Date.now(), ...prevState];
    });
  }, []);

  const keyExtractor = React.useCallback((item: any) => {
    return item.toString();
  }, []);

  const renderCell = React.useCallback(
    (props: any) => (
      <Animated.View
         {...props}
          layout={Layout.springify()}
          entering={ZoomIn}
          exiting={ZoomOut}/>
    ),
    [],
  );

  return (
    <View style={{flex:1}} >
      <Button title="ADD ITEM" onPress={onPress} />
      <AnimatedFlatList
        contentContainerStyle={{ paddingHorizontal: 24 }}
        data={items}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        CellRendererComponent={renderCell}
      />
    </View>
  );
}

Here is visual results:

Android iOS

This worked for me. Thank you

Latropos commented 10 months ago

I can confirmed that in discussion there are several solution fixing this issue. We are not going to fix issues with layout animations for Reanimated 2 - please use any of provided workaround or try using Reanimated 3.

Latropos commented 10 months ago

You can also fix the problem using prop itemLayoutAnimation: Layout.Springify() in your AnimatedFlatList.

cbargren commented 10 months ago

Hi everyone, entering and exiting animations are not enough for list items animation's harmony and itemLayoutAnimation is not working with createAnimatedComponent function. Therefore, you should override the CellRendererComponent property of the animated list, see the example below for an animated flatlist, it is working fine on both Android and iOS. {...}

This solution is great! But it doesn't seem to work if you're using numColumns={2}. When you have that set, it only shows the entering/exiting animations, not the layout change animations. Does anyone have a solution that works with layout animations as well with multiple columns?

Bi0max commented 4 months ago

You can also fix the problem using prop itemLayoutAnimation: Layout.Springify() in your AnimatedFlatList.

Hi @Latropos , this still does not work on Android. I have Reanimated version 3.6.2.

NiccoloCase commented 1 month ago

same issue. with numColumns it doesn't work