Expandable ListView is the common Multiple ListView structure used in hundreds of mobile applications. Expandable ListView is a expandable collapsible vertical listview structure used to show multiple category and sub category wise list structure. It is also known as two level vertical scrolling listview. It is differ from simple ListView because in simple ListView or FlatList we will only show single level items but in Expandable ListView we will show multiple level items. Currently there is no pure component available in react native to create Expandable ListView but using the custom component designing functionality we can easily create our own custom Expandable ListView. In this tutorial we would create Expandable ListView in Android iOS app example with tutorial.
Live screenshot of App:
Contents in this project React Native Expandable ListView in Android iOS Example Tutorial.
1. Import Alert, LayoutAnimation, StyleSheet, View, Text, ScrollView, UIManager, TouchableOpacity, Platform, Image component in your App.js file.
1 2 3 |
import React, { Component } from 'react'; import { Alert, LayoutAnimation, StyleSheet, View, Text, ScrollView, UIManager, TouchableOpacity, Platform, Image } from 'react-native'; |
2. Create a class named as Expandable_ListView. This class would be our main custom Expandable ListView class.
1 2 3 4 5 |
class Expandable_ListView extends Component { } |
3. Create constructor() in Expandable_ListView class. In the constructor we would make a State named as layout_Height and set the default height as 0 Zero.
1 2 3 4 5 6 7 8 9 10 |
constructor() { super(); this.state = { layout_Height: 0 } } |
4. Create componentWillReceiveProps() with nextProps parameter in Expandable_ListView class. In this method we would simply put a if condition on Item expand time and according to that set the layout_height state value. The componentWillReceiveProps method is used to re-compute the data when a prop value is changed in react native.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
componentWillReceiveProps(nextProps) { if (nextProps.item.expanded) { this.setState(() => { return { layout_Height: null } }); } else { this.setState(() => { return { layout_Height: 0 } }); } } |
5. Create shouldComponentUpdate() inbuilt method with nextProps & nextState parameter in Expandable_ListView class. In this function we would compare the layout_height state value and according to that return true or false value. The shouldComponentUpdate() function is used to increase the app performance by disabling all the items re-rendering each time when user clicks a data. It will make sure the selected items data is loaded only not the complete List.
1 2 3 4 5 6 |
shouldComponentUpdate(nextProps, nextState) { if (this.state.layout_Height !== nextState.layout_Height) { return true; } return false; } |
6. Create a function named as show_Selected_Category() with item parameter in Expandable_ListView class. This function is used to show the selected item of Expandable List View.
1 2 3 4 5 6 |
show_Selected_Category = (item) => { // Write your code here which you want to execute on sub category selection. Alert.alert(item); } |
7. Now finally we would create the Expandable ListView header and items View in render’s return block in Expandable_ListView 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 |
render() { return ( <View style={styles.Panel_Holder}> <TouchableOpacity activeOpacity={0.8} onPress={this.props.onClickFunction} style={styles.category_View}> <Text style={styles.category_Text}>{this.props.item.category_Name} </Text> <Image source={{ uri: 'https://reactnativecode.com/wp-content/uploads/2019/02/arrow_right_icon.png' }} style={styles.iconStyle} /> </TouchableOpacity> <View style={{ height: this.state.layout_Height, overflow: 'hidden' }}> { this.props.item.sub_Category.map((item, key) => ( <TouchableOpacity key={key} style={styles.sub_Category_Text} onPress={this.show_Selected_Category.bind(this, item.name)}> <Text> {item.name} </Text> <View style={{ width: '100%', height: 1, backgroundColor: '#000' }} /> </TouchableOpacity> )) } </View> </View> ); } |
8. Complete source code for Expandable_ListView 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 70 71 72 73 74 75 76 77 78 79 80 81 |
class Expandable_ListView extends Component { constructor() { super(); this.state = { layout_Height: 0 } } componentWillReceiveProps(nextProps) { if (nextProps.item.expanded) { this.setState(() => { return { layout_Height: null } }); } else { this.setState(() => { return { layout_Height: 0 } }); } } shouldComponentUpdate(nextProps, nextState) { if (this.state.layout_Height !== nextState.layout_Height) { return true; } return false; } show_Selected_Category = (item) => { // Write your code here which you want to execute on sub category selection. Alert.alert(item); } render() { return ( <View style={styles.Panel_Holder}> <TouchableOpacity activeOpacity={0.8} onPress={this.props.onClickFunction} style={styles.category_View}> <Text style={styles.category_Text}>{this.props.item.category_Name} </Text> <Image source={{ uri: 'https://reactnativecode.com/wp-content/uploads/2019/02/arrow_right_icon.png' }} style={styles.iconStyle} /> </TouchableOpacity> <View style={{ height: this.state.layout_Height, overflow: 'hidden' }}> { this.props.item.sub_Category.map((item, key) => ( <TouchableOpacity key={key} style={styles.sub_Category_Text} onPress={this.show_Selected_Category.bind(this, item.name)}> <Text> {item.name} </Text> <View style={{ width: '100%', height: 1, backgroundColor: '#000' }} /> </TouchableOpacity> )) } </View> </View> ); } } |
9. Create constructor() in your app’s main default App class. Here we would enable the layout animation.
1 2 3 4 5 6 7 8 |
constructor() { super(); if (Platform.OS === 'android') { UIManager.setLayoutAnimationEnabledExperimental(true) } |
10. Create a constant array named as array in app’s main App class. The array contain our main Expandable ListView items & at the last of array we would create a State named as AccordionData and store the array in this 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 27 28 29 30 31 32 33 34 35 |
const array = [ { expanded: false, category_Name: "Mobiles", sub_Category: [{ id: 1, name: 'Mi' }, { id: 2, name: 'RealMe' }, { id: 3, name: 'Samsung' }, { id: 4, name: 'Infinix' }, { id: 5, name: 'Oppo' }, { id: 6, name: 'Apple' }, { id: 7, name: 'Honor' }] }, { expanded: false, category_Name: "Laptops", sub_Category: [{ id: 8, name: 'Dell' }, { id: 9, name: 'MAC' }, { id: 10, name: 'HP' }, { id: 11, name: 'ASUS' }] }, { expanded: false, category_Name: "Computer Accessories", sub_Category: [{ id: 12, name: 'Pendrive' }, { id: 13, name: 'Bag' }, { id: 14, name: 'Mouse' }, { id: 15, name: 'Keyboard' }] }, { expanded: false, category_Name: "Home Entertainment", sub_Category: [{ id: 16, name: 'Home Audio Speakers' }, { id: 17, name: 'Home Theatres' }, { id: 18, name: 'Bluetooth Speakers' }, { id: 19, name: 'DTH Set Top Box' }] }, { expanded: false, category_Name: "TVs by brand", sub_Category: [{ id: 20, name: 'Mi' }, { id: 21, name: 'Thomson' }, { id: 22, name: 'LG' }, { id: 23, name: 'SONY' }] }, { expanded: false, category_Name: "Kitchen Appliances", sub_Category: [{ id: 24, name: 'Microwave Ovens' }, { id: 25, name: 'Oven Toaster Grills (OTG)' }, { id: 26, name: 'Juicer/Mixer/Grinder' }, { id: 27, name: 'Electric Kettle' }] } ]; this.state = { AccordionData: [...array] } } |
11. Create a function named as update_Layout() with index parameter in main App class. In this method we first enable the layout animation and then update the layout with new items.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
update_Layout = (index) => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); const array = [...this.state.AccordionData]; array[index]['expanded'] = !array[index]['expanded']; this.setState(() => { return { AccordionData: array } }); } |
12. Finally we would call the Expandable_ListView class component in render’s return block in App class. We would call the custom component in ScrollView.
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) => ( <Expandable_ListView key={item.category_Name} onClickFunction={this.update_Layout.bind(this, key)} item={item} /> )) } </ScrollView> </View> ); } |
13. 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
const styles = StyleSheet.create({ MainContainer: { flex: 1, justifyContent: 'center', paddingTop: (Platform.OS === 'ios') ? 20 : 0, backgroundColor: '#F5FCFF', }, iconStyle: { width: 30, height: 30, justifyContent: 'flex-end', alignItems: 'center', tintColor: '#fff' }, sub_Category_Text: { fontSize: 18, color: '#000', padding: 10 }, category_Text: { textAlign: 'left', color: '#fff', fontSize: 21, padding: 10 }, category_View: { marginVertical: 5, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', backgroundColor: '#0091EA' }, Btn: { padding: 10, backgroundColor: '#FF6F00' } }); |
14. 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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
import React, { Component } from 'react'; import { Alert, LayoutAnimation, StyleSheet, View, Text, ScrollView, UIManager, TouchableOpacity, Platform, Image } from 'react-native'; class Expandable_ListView extends Component { constructor() { super(); this.state = { layout_Height: 0 } } componentWillReceiveProps(nextProps) { if (nextProps.item.expanded) { this.setState(() => { return { layout_Height: null } }); } else { this.setState(() => { return { layout_Height: 0 } }); } } shouldComponentUpdate(nextProps, nextState) { if (this.state.layout_Height !== nextState.layout_Height) { return true; } return false; } show_Selected_Category = (item) => { // Write your code here which you want to execute on sub category selection. Alert.alert(item); } render() { return ( <View style={styles.Panel_Holder}> <TouchableOpacity activeOpacity={0.8} onPress={this.props.onClickFunction} style={styles.category_View}> <Text style={styles.category_Text}>{this.props.item.category_Name} </Text> <Image source={{ uri: 'https://reactnativecode.com/wp-content/uploads/2019/02/arrow_right_icon.png' }} style={styles.iconStyle} /> </TouchableOpacity> <View style={{ height: this.state.layout_Height, overflow: 'hidden' }}> { this.props.item.sub_Category.map((item, key) => ( <TouchableOpacity key={key} style={styles.sub_Category_Text} onPress={this.show_Selected_Category.bind(this, item.name)}> <Text> {item.name} </Text> <View style={{ width: '100%', height: 1, backgroundColor: '#000' }} /> </TouchableOpacity> )) } </View> </View> ); } } export default class App extends Component { constructor() { super(); if (Platform.OS === 'android') { UIManager.setLayoutAnimationEnabledExperimental(true) } const array = [ { expanded: false, category_Name: "Mobiles", sub_Category: [{ id: 1, name: 'Mi' }, { id: 2, name: 'RealMe' }, { id: 3, name: 'Samsung' }, { id: 4, name: 'Infinix' }, { id: 5, name: 'Oppo' }, { id: 6, name: 'Apple' }, { id: 7, name: 'Honor' }] }, { expanded: false, category_Name: "Laptops", sub_Category: [{ id: 8, name: 'Dell' }, { id: 9, name: 'MAC' }, { id: 10, name: 'HP' }, { id: 11, name: 'ASUS' }] }, { expanded: false, category_Name: "Computer Accessories", sub_Category: [{ id: 12, name: 'Pendrive' }, { id: 13, name: 'Bag' }, { id: 14, name: 'Mouse' }, { id: 15, name: 'Keyboard' }] }, { expanded: false, category_Name: "Home Entertainment", sub_Category: [{ id: 16, name: 'Home Audio Speakers' }, { id: 17, name: 'Home Theatres' }, { id: 18, name: 'Bluetooth Speakers' }, { id: 19, name: 'DTH Set Top Box' }] }, { expanded: false, category_Name: "TVs by brand", sub_Category: [{ id: 20, name: 'Mi' }, { id: 21, name: 'Thomson' }, { id: 22, name: 'LG' }, { id: 23, name: 'SONY' }] }, { expanded: false, category_Name: "Kitchen Appliances", sub_Category: [{ id: 24, name: 'Microwave Ovens' }, { id: 25, name: 'Oven Toaster Grills (OTG)' }, { id: 26, name: 'Juicer/Mixer/Grinder' }, { id: 27, name: 'Electric Kettle' }] } ]; this.state = { AccordionData: [...array] } } update_Layout = (index) => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); const array = [...this.state.AccordionData]; array[index]['expanded'] = !array[index]['expanded']; this.setState(() => { return { AccordionData: array } }); } render() { return ( <View style={styles.MainContainer}> <ScrollView contentContainerStyle={{ paddingHorizontal: 10, paddingVertical: 5 }}> { this.state.AccordionData.map((item, key) => ( <Expandable_ListView key={item.category_Name} 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, backgroundColor: '#F5FCFF', }, iconStyle: { width: 30, height: 30, justifyContent: 'flex-end', alignItems: 'center', tintColor: '#fff' }, sub_Category_Text: { fontSize: 18, color: '#000', padding: 10 }, category_Text: { textAlign: 'left', color: '#fff', fontSize: 21, padding: 10 }, category_View: { marginVertical: 5, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', backgroundColor: '#0091EA' }, Btn: { padding: 10, backgroundColor: '#FF6F00' } }); |
Screenshots:
hi sir excellent tutorial , i am same work but using visibility true and false , you are doing a great work
Welcome Rahul 🙂
i try’ed your tutorial ( React Native Infinite List FlatList Pagination)
on that the list is not found stopping at the end ). please provide a solution for this. can you possible to provide a tutorial on automatic loading infinite flat list view (by eliminating button) . i fount more example but none of them are working properly . thank you for support
Jithin i please explain your question more briefly.
Sorry for the inconvenience I solved the issue. Thank you for the support
Welcome Jithin 🙂 .
Hello Brother,
Thank you! very nice example it’s saved my day.
Welcome bro 🙂 .
Hello Sir,
Hope you are doing well!
I am using this code and it’s working fine. I want to use this in a different way. what I want ->
I need to to use this as parent-child list with checkbox means –
parent checkbox 1
-child checkbox 1
-child checkbox 2
-child checkbox 3
-child checkbox 4
parent checkbox 2
-child checkbox 1
-child checkbox 2
-child checkbox 3
-child checkbox 4
I have used checkbox instead of text. Now issue is how to manage the checked values from the array?
Parteek you can read my this tutorial this would help you : https://reactnativecode.com/custom-checkbox-component/ .
Greeting Admin,
Can we retrieve the list of data in array from database eg. mysql? If can, how? As for the example it has been listed in the code.
Colin you simply want to make the expandable listview with JSON data, am i right ?
Yes, you are right, sir. JSON Expandable ListView.
Colin i will soon publish a new tutorial regarding to JSON listview 🙂 .
And I don’t know how to do it sir. Can you show me some example
Colin i will soon publish a new tutorial regarding to your query 🙂 .
Thank you very much Sir .
Hi Sir.. Ty For your Clean Tutorial, i have an issue if i use
” this.props.navigation.navigate(”)” in ”
show_Selected_Category ” from class ExpandableListView i get an issue cannot read property ‘navigate’ of undefined.
how to fix this one…
show_Selected_Category = (item) => {
this.props.navigation.navigate(‘Main’);
}
i also have issue with i have to open another screen when i select sub category help me…….
What issue you are facing bhautik ?
Hi,
I am wondering if it is possible (for sure it is possible :D) to do a multi-select on the embedded list, so the part that can be hidden (the collapsable part).
p.s. instead of .map() I used FlatList. Your tutorial is pretty awesome, maybe you can add FlatList so people can implement that too?
Thanks in advance!
Thanks for comment Bob, i’ll soon make a tutorial with multiple items selections 🙂 .
Hi admin , thank you for the great tutorial I am facing an issue while I have added a picker inside the header , the value of the picker is not updating the value until I expand it Can you help me out with this asap .
like I add this simple picker =>
{this.props.item.category_Name}
this.setState({language: itemValue})
}>
hi snehal , i have used this code and put some buttons inside it, but whenever i am pressing any button it cannot changes its color during the onpress but it changes when i press to expandable component, so how to do it …. i am sharing snack expo link here
https://snack.expo.io/@hemant_vivek/expandable_butts
Hello!
You have created a beautiful component. Thanks for publishing it.
can see how one can execute a Javascript function, like Alert(), upon sub item selection.
What I would MOST like to do is use your Expandable ListView as a navigation menu, where clicking a sub item navigates to a screen/component that is in a separate .js component file.
Can you do that with this? If so, how?
Would love to see an example of each sub item bringing up a different imported screen/component.
Thanks.
Hello sir,How can we expand a perticulaer selected subcatogory when we navigates fro clid screen to expandable listview screen.
Please refer this for my question.
https://stackoverflow.com/questions/63752319/how-to-expand-a-row-in-expandable-listview-in-react-native-mobile-app-manually
Thanks for comment Priyanka, I am checking your code and question please give me 1 day to find the solution 🙂 .
Hi friend. You can put this same example but using JSON and Hooks? Thank you.