react-nativeexporeact-native-flatlistreact-native-reanimatedreact-animated

ReactNative FlatList: fluid expansion of an item in an absolute layover


I am trying to implement a behaviour that is similar to the weather app on iOS: one has a list of city tiles.

Upon clicking on one of the cities, it expands and grows in size, to overlay the list screen and present the details for the weather in that city.

It also collapses back into a list item when the 'back' button is pressed.

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

I've achieved a similar behaviour by having an absolute overlay of a duplicate of that city tile matching the exact position of the original tile.

Then there is a withTiming animation that expands the absolute overlay to make it occupy the full height of the screen.

It works, BUT it is janky, especially if the overlay contains some other data that is being fetched and then populates the details overlay.

Is there a better way to achieve more smoothness?


Solution

  • these kinds of animations are built differently. If we look at an iOS/Android Native code example, each screen is a UIViewController or Activity. So you should have 2 separate screens to do it properly, the first screen will be a list of your locations and the second screen will be the details of each location. The technique is called shared element transition. I believe the simplest way to achieve this is by using react-navigation. While back they didn't have support for native stack screens (which brings better performance), but on version 7 they have added it.

    https://reactnavigation.org/docs/7.x/shared-element-transitions/

    function LocationScreen({ navigation }) {
      return (
        <View style={styles.container}>
          <Animated.Image
            source={{ uri: 'your-location-image' }}
            style={{ width: 300, height: 100 }}
            sharedTransitionTag="location-1"
          />
        </View>
      );
    }
    
    function LocationDetail({ navigation }) {
      return (
        <View style={styles.container}>
          <Animated.Image
            source={{ uri: 'your-location-image' }}
            style={{ width: 300, height: 500 }}
            sharedTransitionTag="location-1"
          />
        </View>
      );
    }
    

    You can even customize the transition https://reactnavigation.org/docs/shared-element-transitions#customizing-the-transition