Accordion Panels is animated panels with Collapsible and Expandable functionality. Accordion panels are automatically controlled by itself and if user clicks on single panel then it will smoothly slide down and open itself but if you have clicked on any closed panel then it will first close the already open panel and then open the clicked panel using animation. We are using React Native’s Layout Animation to to open and close the panel. So in this tutorial we would going to implement Animated Accordion Collapsible Expandable Panel iOS Android Example Tutorial.
What we are doing in this project:
We are creating our own custom component to make animated Accordion effect now all we have to do is pass the data from the below class using Array and the data would automatically filled inside the Accordion.
To know how exactly it will work on your device see the below live screenshot:
Contents in this project React Native Animated Accordion Collapsible Expandable Panel iOS Android Example Tutorial:
1. Import Platform, LayoutAnimation, StyleSheet, View, Text, ScrollView, UIManager and TouchableOpacity component in your project.
1 2 3 |
import React, { Component } from 'react'; import { Platform, LayoutAnimation, StyleSheet, View, Text, ScrollView, UIManager, TouchableOpacity } from 'react-native'; |
2. Create a New class named as Accordion_Panel , This class is our custom Accordion class and used to render the complete Accordion Panel on device screen.
1 2 3 4 5 |
class Accordion_Panel extends Component { } |
3. Create constructor() method in Accordion_Panel class with a State named as updated_Height. The updated_Height state is used to manage the height of the Accordion according to content present inside it.
1 2 3 4 5 6 7 8 9 10 |
constructor() { super(); this.state = { updated_Height: 0 } } |
4. Create componentWillReceiveProps() inbuilt life cycle method inside the Accordion_Panel class. Using this method we would check the newly updated height of Accordion and updating the state. If you wish to study all the React Native life cycle methods then read our this tutorial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
componentWillReceiveProps(update_Props) { if (update_Props.item.expanded) { this.setState(() => { return { updated_Height: null } }); } else { this.setState(() => { return { updated_Height: 0 } }); } } |
5. Creating react’s own shouldComponentUpdate() method inside the Accordion_Panel class. Using this method we would improve our application experience and increasing its memory management power so it can be run more smoothly on your device with thousands of items inside it. It will stop our application from lagging or hanging with more data present condition. This method returns Boolean value.
1 2 3 4 5 6 7 8 9 10 11 |
shouldComponentUpdate(update_Props, nextState) { if (update_Props.item.expanded !== this.props.item.expanded) { return true; } return false; } |
6. Create the complete view of Accordion_Panel class in render’s return block . We are creating our own props this.props.onClickFunction , this.props.item.title and this.props.item.body inside the design layout. We would pass the props value from our next main class .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
render() { return ( <View style={styles.Panel_Holder}> <TouchableOpacity activeOpacity={0.7} onPress={this.props.onClickFunction} style={styles.Btn}> <Text style={styles.Panel_Button_Text}>{this.props.item.title} </Text> </TouchableOpacity> <View style={{ height: this.state.updated_Height, overflow: 'hidden' }}> <Text style={styles.Panel_text}> {this.props.item.body} </Text> </View> </View> ); } |
7. Complete source code for Accordion_Panel class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
class Accordion_Panel extends Component { constructor() { super(); this.state = { updated_Height: 0 } } componentWillReceiveProps(update_Props) { if (update_Props.item.expanded) { this.setState(() => { return { updated_Height: null } }); } else { this.setState(() => { return { updated_Height: 0 } }); } } shouldComponentUpdate(update_Props, nextState) { if (update_Props.item.expanded !== this.props.item.expanded) { return true; } return false; } render() { return ( <View style={styles.Panel_Holder}> <TouchableOpacity activeOpacity={0.7} onPress={this.props.onClickFunction} style={styles.Btn}> <Text style={styles.Panel_Button_Text}>{this.props.item.title} </Text> </TouchableOpacity> <View style={{ height: this.state.updated_Height, overflow: 'hidden' }}> <Text style={styles.Panel_text}> {this.props.item.body} </Text> </View> </View> ); } } |
8. Creating our main class named as App . This would be our Main default export class.
1 2 3 4 5 6 |
export default class App extends Component { } |
9. Creating constructor() inside the App class and put the Layout Animation enable method. This method would enable the layout animation effect in your current project. No we would make a constant Array with 10 items and inside this we would set the default Accordion panel opening status and Accordion Title and Accordion body text. After that finally we would make a State named as AccordionData and set the Array inside the state.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
constructor() { super(); if (Platform.OS === 'android') { UIManager.setLayoutAnimationEnabledExperimental(true) } const array = [ { expanded: false, title: "Panel 1", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 2", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 3", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 4", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 5", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 6", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 7", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 8", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 9", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 10", body: "Hello Guys this is the Animated Accordion Panel." }, ]; this.state = { AccordionData: [...array] } } |
10. Create a function named as update_Layout inside the App class. We would call this function on Accordion item title clicked event. Using this function we would expand and collapse the Accordion panel and update the State value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
update_Layout = (index) => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); const array = this.state.AccordionData.map((item) => { const newItem = Object.assign({}, item); newItem.expanded = false; return newItem; }); array[index].expanded = true; this.setState(() => { return { AccordionData: array } }); } |
11. Create the Root view inside render’s return block and call the Accordion_Panel class with Array data. We would call the Accordion_Panel class inside a ScrollView component.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
render() { return ( <View style={styles.MainContainer}> <ScrollView contentContainerStyle={{ paddingHorizontal: 10, paddingVertical: 5 }}> { this.state.AccordionData.map((item, key) => ( <Accordion_Panel key={key} onClickFunction={this.update_Layout.bind(this, key)} item={item} /> )) } </ScrollView> </View> ); } |
12. Creating Style.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
const styles = StyleSheet.create({ MainContainer: { flex: 1, justifyContent: 'center', paddingTop: (Platform.OS === 'ios') ? 20 : 0 }, Panel_text: { fontSize: 18, color: '#000', padding: 10 }, Panel_Button_Text: { textAlign: 'center', color: '#fff', fontSize: 21 }, Panel_Holder: { borderWidth: 1, borderColor: '#FF6F00', marginVertical: 5 }, Btn: { padding: 10, backgroundColor: '#FF6F00' } }); |
13. Complete source code for App.js File :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
import React, { Component } from 'react'; import { Platform, LayoutAnimation, StyleSheet, View, Text, ScrollView, UIManager, TouchableOpacity } from 'react-native'; class Accordion_Panel extends Component { constructor() { super(); this.state = { updated_Height: 0 } } componentWillReceiveProps(update_Props) { if (update_Props.item.expanded) { this.setState(() => { return { updated_Height: null } }); } else { this.setState(() => { return { updated_Height: 0 } }); } } shouldComponentUpdate(update_Props, nextState) { if (update_Props.item.expanded !== this.props.item.expanded) { return true; } return false; } render() { return ( <View style={styles.Panel_Holder}> <TouchableOpacity activeOpacity={0.7} onPress={this.props.onClickFunction} style={styles.Btn}> <Text style={styles.Panel_Button_Text}>{this.props.item.title} </Text> </TouchableOpacity> <View style={{ height: this.state.updated_Height, overflow: 'hidden' }}> <Text style={styles.Panel_text}> {this.props.item.body} </Text> </View> </View> ); } } export default class App extends Component { constructor() { super(); if (Platform.OS === 'android') { UIManager.setLayoutAnimationEnabledExperimental(true) } const array = [ { expanded: false, title: "Panel 1", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 2", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 3", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 4", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 5", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 6", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 7", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 8", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 9", body: "Hello Guys this is the Animated Accordion Panel." }, { expanded: false, title: "Panel 10", body: "Hello Guys this is the Animated Accordion Panel." }, ]; this.state = { AccordionData: [...array] } } update_Layout = (index) => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); const array = this.state.AccordionData.map((item) => { const newItem = Object.assign({}, item); newItem.expanded = false; return newItem; }); array[index].expanded = true; this.setState(() => { return { AccordionData: array } }); } render() { return ( <View style={styles.MainContainer}> <ScrollView contentContainerStyle={{ paddingHorizontal: 10, paddingVertical: 5 }}> { this.state.AccordionData.map((item, key) => ( <Accordion_Panel key={key} onClickFunction={this.update_Layout.bind(this, key)} item={item} /> )) } </ScrollView> </View> ); } } const styles = StyleSheet.create({ MainContainer: { flex: 1, justifyContent: 'center', paddingTop: (Platform.OS === 'ios') ? 20 : 0 }, Panel_text: { fontSize: 18, color: '#000', padding: 10 }, Panel_Button_Text: { textAlign: 'center', color: '#fff', fontSize: 21 }, Panel_Holder: { borderWidth: 1, borderColor: '#FF6F00', marginVertical: 5 }, Btn: { padding: 10, backgroundColor: '#FF6F00' } }); |
Screenshot:
How can we create header in accordion (Panel 2,panel 3 etc) ?
Sneha there is already a header in all the panels, please explain your question more briefly ?
when we add more data in body then accordion headers are not sticky. I want sticky headers at the time of scrolling data.
Sneha you want to to make the only opening header sticky or the top one please be more specific ?
I am having an error.
error: bundling failed: SyntaxError: c:\test\node_modules\react-native\scripts\src\components\Help.js: Unexpected token, expected “}” (127:15)
125 | const styles = StyleSheet.create({
126 |
> 127 | MainContainer: {
| ^
128 | flex: 1,
129 | justifyContent: ‘center’,
130 | paddingTop: (Platform.OS === ‘ios’) ? 20 : 0
at _class.raise (c:\test\node_modules\@babel\parser\lib\index.js:4028:15)
at _class.unexpected (c:\test\node_modules\@babel\parser\lib\index.js:5359:16)
at _class.expect (c:\test\node_modules\@babel\parser\lib\index.js:5347:28)
at _class.jsxParseExpressionContainer (c:\test\node_modules\@babel\parser\lib\index.js:3583:12)
at _class.jsxParseElementAt (c:\test\node_modules\@babel\parser\lib\index.js:3670:36)
at _class.jsxParseElement (c:\test\node_modules\@babel\parser\lib\index.js:3712:19)
at _class.parseExprAtom (c:\test\node_modules\@babel\parser\lib\index.js:3719:21)
at _class.parseExprSubscripts (c:\test\node_modules\@babel\parser\lib\index.js:6081:21)
at _class.parseMaybeUnary (c:\test\node_modules\@babel\parser\lib\index.js:6060:21)
at _class.parseExprOps (c:\test\node_modules\@babel\parser\lib\index.js:5945:21)
thank you
Try to re-install the app and you have missed putting } bracket in your code.
can u please help me to implement list of elements under each separate panel.????and also drop down icons when we open any particulate panel..????
Shraddha you want to put list items under each panel ?
How to implement collapsible Expandable like above and instate of showing text, we need to put input text to get input from user and collapse and submit with the data.