Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Collapsible Card with React Native Reanimated
Dima Portenko
Dima Portenko

Posted on • Edited on

     

Collapsible Card with React Native Reanimated

In this tutorial, we'll create an animated collapsable card usingreact-native-reanimated. We'll be starting from a provided template, which can be found atthis GitHub link. The template contains an Expo project with a FlatList. Each list item has an image, title, and description. Our goal is to make the description collapsable with a smooth animation.

Demo gif

## Getting Started

First, clone the project from GitHub and switch to thetemplate branch:

git clone https://github.com/dimaportenko/reanimated-collapsable-card-tutorial.gitcd reanimated-collapsable-card-tutorialgit checkout template
Enter fullscreen modeExit fullscreen mode

Adding React Native Reanimated

We will be using thereact-native-reanimated library to create our animations. To add it, run the following command:

npx expo install react-native-reanimated
Enter fullscreen modeExit fullscreen mode

Then, you'll need to update yourbabel.config.js:

module.exports=function(api){api.cache(true);return{presets:['babel-preset-expo'],plugins:['react-native-reanimated/plugin'],};};
Enter fullscreen modeExit fullscreen mode

Implementation

In ourListItem.tsx, we will add a new state for the height of the collapsable content:

const[height,setHeight]=useState(0);constanimatedHeight=useSharedValue(0);
Enter fullscreen modeExit fullscreen mode

We calculate the collapsible content height in theonLayout callback:

constonLayout=(event:LayoutChangeEvent)=>{constonLayoutHeight=event.nativeEvent.layout.height;if(onLayoutHeight>0&&height!==onLayoutHeight){setHeight(onLayoutHeight);}};
Enter fullscreen modeExit fullscreen mode

We'll create an animated style for our collapsable content:

constcollapsableStyle=useAnimatedStyle(()=>{animatedHeight.value=expanded?withTiming(height):withTiming(0);return{height:animatedHeight.value,};},[expanded,height]);
Enter fullscreen modeExit fullscreen mode

We'll wrap our collapsable content in anAnimated.View:

<Animated.Viewstyle={[collapsableStyle,{overflow:'hidden'}]}><Viewstyle={{position:'absolute'}}onLayout={onLayout}><Textstyle={[styles.details,styles.text]}>{item.details}</Text></View></Animated.View>
Enter fullscreen modeExit fullscreen mode

To make our code more maintainable, let's refactor theCollapsableContainer into a separate reusable component:

importReact,{useState}from"react";import{LayoutChangeEvent,View,Text}from"react-native";importAnimated,{useAnimatedStyle,useSharedValue,withTiming,}from"react-native-reanimated";exportconstCollapsableContainer=({children,expanded,}:{children:React.ReactNode;expanded:boolean;})=>{const[height,setHeight]=useState(0);constanimatedHeight=useSharedValue(0);constonLayout=(event:LayoutChangeEvent)=>{constonLayoutHeight=event.nativeEvent.layout.height;if(onLayoutHeight>0&&height!==onLayoutHeight){setHeight(onLayoutHeight);}};constcollapsableStyle=useAnimatedStyle(()=>{animatedHeight.value=expanded?withTiming(height):withTiming(0);return{height:animatedHeight.value,};},[expanded,height]);return(<Animated.Viewstyle={[collapsableStyle,{overflow:"hidden"}]}><Viewstyle={{position:"absolute"}}onLayout={onLayout}>{children}</View></Animated.View>);};
Enter fullscreen modeExit fullscreen mode

Then, we can use our newCollapsableContainer component in theListItem component:

exportconstListItem=({item}:{item:ListItemType})=>{const[expanded,setExpanded]=useState(false);constonItemPress=()=>{setExpanded(!expanded);};return(<Viewstyle={styles.wrap}><TouchableWithoutFeedbackonPress={onItemPress}><Viewstyle={styles.container}><Imagesource={{uri:item.image}}style={styles.image}/><Viewstyle={styles.textContainer}><Textstyle={styles.text}>{item.title}</Text><Textstyle={styles.text}>{item.subtitle}</Text></View></View></TouchableWithoutFeedback><CollapsableContainerexpanded={expanded}><Textstyle={[styles.details,styles.text]}>{item.details}</Text></CollapsableContainer></View>);};
Enter fullscreen modeExit fullscreen mode

That's it! You have successfully created an animated collapsable card in React Native usingreact-native-reanimated. This animated component provides a smooth user experience, and the separateCollapsableContainer component can be reused in different parts of your application. Happy coding!Final code

Top comments(6)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
skizzo profile image
Stephan Müller
  • Joined

Hi,
first of all, thank you for the great tutorial!

The only problem I run into is when the "expanded" value is set to true on mount, nothing is displayed, I think it's because thecollapsableStyle is not recomputed.

How could this be resolved?

CollapseExpand
 
dimaportenko profile image
Dima Portenko
Hey there 👋 . I'm Dima Portenko. I'm doing software development for over a decade. Right now I'm sticking with React Native development. Follow my YouTube channel and Twitter account to learn more.
  • Location
    Kharkiv, Ukraine
  • Joined

Hey,
try to add height to the dependencies like

 const collapsableStyle = useAnimatedStyle(() => {    animatedHeight.value = expanded ? withTiming(height) : withTiming(0);    return {      height: animatedHeight.value,    };  }, [expanded, height]);
Enter fullscreen modeExit fullscreen mode

I believe on the first renderheight is 0 and it's not triggered to recalculate on height value updated.

CollapseExpand
 
scalfs profile image
Vitor Escalfoni
  • Location
    Vila Velha, Brazil
  • Work
    Mobile Developer at SlabWare
  • Joined

Thanks man! I had a similar solution, but what did the trick for me was your suggestion of using { overflow: 'hidden' }, to ensure we would get the full height.

CollapseExpand
 
dimaportenko profile image
Dima Portenko
Hey there 👋 . I'm Dima Portenko. I'm doing software development for over a decade. Right now I'm sticking with React Native development. Follow my YouTube channel and Twitter account to learn more.
  • Location
    Kharkiv, Ukraine
  • Joined

Welcome!

CollapseExpand
 
wmonecke profile image
Walter Monecke
  • Joined

I dont know what I am doing wrong but this is not working for me.

CollapseExpand
 
dimaportenko profile image
Dima Portenko
Hey there 👋 . I'm Dima Portenko. I'm doing software development for over a decade. Right now I'm sticking with React Native development. Follow my YouTube channel and Twitter account to learn more.
  • Location
    Kharkiv, Ukraine
  • Joined

if you'll share your code on github or expo snack, I can take a look.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Hey there 👋 . I'm Dima Portenko. I'm doing software development for over a decade. Right now I'm sticking with React Native development. Follow my YouTube channel and Twitter account to learn more.
  • Location
    Kharkiv, Ukraine
  • Joined

More fromDima Portenko

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp