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: ‘/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: ‘/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: ‘/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: