Vertical Step indicator
May 06, 2023
1 min
A Picker component for React Native which emulates the native <select>
interfaces for iOS and Android
For iOS, by default we are wrapping an unstyled TextInput component. You can then pass down styles to customize it to your needs.
For Android, by default we are using the native Picker component. If you prefer, you can set useNativeAndroidPickerStyle to false, which will also render an unstyled TextInput component. You can then pass down styles to customize it to your needs.
For either platform, you can alternatively pass down a child element of your choice that will be wrapped in a touchable area.
npm install react-native-picker-select
npm install @react-native-picker/picker npx pod-install
expo install @react-native-picker/picker
-import React from 'react'; import { Button, Text, TextInput, Platform, ScrollView, StyleSheet, TouchableWithoutFeedback, View, } from 'react-native'; import { Chevron } from 'react-native-shapes'; import { Ionicons } from '@expo/vector-icons'; import RNPickerSelect, { defaultStyles } from 'react-native-picker-select'; // import RNPickerSelect, { defaultStyles } from './debug'; const sports = [ { label: 'Football', value: 'football', }, { label: 'Baseball', value: 'baseball', }, { label: 'Hockey', value: 'hockey', }, ]; export default class App extends React.Component { constructor(props) { super(props); this.inputRefs = { firstTextInput: null, favSport0: null, favSport1: null, lastTextInput: null, favSport5: null, }; this.state = { numbers: [ { label: '1', value: 1, color: 'orange', }, { label: '2', value: 2, color: 'green', }, ], favSport0: undefined, favSport1: undefined, favSport2: undefined, favSport3: undefined, favSport4: 'baseball', previousFavSport5: undefined, favSport5: null, favNumber: undefined, }; this.InputAccessoryView = this.InputAccessoryView.bind(this); } InputAccessoryView() { return ( <View style={defaultStyles.modalViewMiddle}> <TouchableWithoutFeedback onPress={() => { this.setState( { favSport5: this.state.previousFavSport5, }, () => { this.inputRefs.favSport5.togglePicker(true); } ); }} hitSlop={{ top: 4, right: 4, bottom: 4, left: 4 }}> <View testID="needed_for_touchable"> <Text style={[ defaultStyles.done, { fontWeight: 'normal', color: 'red' }, ]}> Cancel </Text> </View> </TouchableWithoutFeedback> <Text>Name | Prefer</Text> <TouchableWithoutFeedback onPress={() => { this.inputRefs.favSport5.togglePicker(true); }} hitSlop={{ top: 4, right: 4, bottom: 4, left: 4 }}> <View testID="needed_for_touchable"> <Text style={defaultStyles.done}>Done</Text> </View> </TouchableWithoutFeedback> </View> ); } render() { const placeholder = { label: 'Select a sport...', value: null, color: '#9EA0A4', }; return ( <View style={styles.container}> <ScrollView style={styles.scrollContainer} contentContainerStyle={styles.scrollContentContainer}> <Text>Standard TextInput</Text> <TextInput ref={el => { this.inputRefs.firstTextInput = el; }} returnKeyType="next" enablesReturnKeyAutomatically onSubmitEditing={() => { this.inputRefs.favSport0.togglePicker(); }} style={ Platform.OS === 'ios' ? pickerSelectStyles.inputIOS : pickerSelectStyles.inputAndroid } blurOnSubmit={false} /> <View paddingVertical={5} /> <Text>useNativeAndroidPickerStyle (default)</Text> {/* and iOS onUpArrow/onDownArrow toggle example */} <RNPickerSelect placeholder={placeholder} items={sports} onValueChange={value => { this.setState({ favSport0: value, }); }} onUpArrow={() => { this.inputRefs.firstTextInput.focus(); }} onDownArrow={() => { this.inputRefs.favSport1.togglePicker(); }} style={pickerSelectStyles} value={this.state.favSport0} ref={el => { this.inputRefs.favSport0 = el; }} /> <View paddingVertical={5} /> <Text>set useNativeAndroidPickerStyle to false</Text> <RNPickerSelect placeholder={placeholder} items={sports} onValueChange={value => { this.setState({ favSport1: value, }); }} style={pickerSelectStyles} value={this.state.favSport1} useNativeAndroidPickerStyle={false} ref={el => { this.inputRefs.favSport1 = el; }} /> <View paddingVertical={5} /> <Text>set placeholder to empty object</Text> {/* and hiding the InputAccessoryView on iOS */} <RNPickerSelect placeholder={{}} items={sports} onValueChange={value => { this.setState({ favSport2: value, }); }} InputAccessoryView={() => null} style={pickerSelectStyles} value={this.state.favSport2} /> <View paddingVertical={5} /> <Text>custom icon using react-native-shapes</Text> {/* and useNativeAndroidPickerStyle={false} with underlineColorAndroid */} <RNPickerSelect placeholder={placeholder} items={sports} onValueChange={value => { this.setState({ favSport3: value, }); }} style={{ inputAndroid: { backgroundColor: 'transparent', }, iconContainer: { top: 5, right: 15, }, }} value={this.state.favSport3} useNativeAndroidPickerStyle={false} textInputProps={{ underlineColorAndroid: 'cyan' }} Icon={() => { return <Chevron size={1.5} color="gray" />; }} /> <View paddingVertical={5} /> <Text>custom icon using react-native-vector-icons</Text> {/* and value defined */} <RNPickerSelect placeholder={placeholder} items={sports} onValueChange={value => { this.setState({ favSport4: value, }); }} style={{ ...pickerSelectStyles, iconContainer: { top: 10, right: 12, }, }} value={this.state.favSport4} useNativeAndroidPickerStyle={false} textInputProps={{ underlineColor: 'yellow' }} Icon={() => { return <Ionicons name="md-arrow-down" size={24} color="gray" />; }} /> <View paddingVertical={5} /> <Text>custom icon using your own css</Text> {/* and placeholder style changes, showing colors on items, useNativeAndroidPickerStyle={false} */} <RNPickerSelect placeholder={{ label: 'Select a number or add another...', value: null, color: 'red', }} items={this.state.numbers} onValueChange={value => { this.setState({ favNumber: value, }); }} style={{ ...pickerSelectStyles, iconContainer: { top: 20, right: 10, }, placeholder: { color: 'purple', fontSize: 12, fontWeight: 'bold', }, }} value={this.state.favNumber} Icon={() => { return ( <View style={{ backgroundColor: 'transparent', borderTopWidth: 10, borderTopColor: 'gray', borderRightWidth: 10, borderRightColor: 'transparent', borderLeftWidth: 10, borderLeftColor: 'transparent', width: 0, height: 0, }} /> ); }} /> <Button title="+1 number to the above list" onPress={() => { const { numbers } = this.state; const value = numbers.length + 1; numbers.push({ label: `${value}`, value, color: 'dodgerblue', }); this.setState({ numbers, }); }} /> <View paddingVertical={5} /> <Text>custom InputAccessoryView on iOS</Text> <RNPickerSelect items={sports} value={this.state.favSport5} onValueChange={value => { this.setState({ favSport5: value, }); }} onOpen={() => { this.setState({ previousFavSport5: this.state.favSport5, }); }} InputAccessoryView={this.InputAccessoryView} ref={ref => { this.inputRefs.favSport5 = ref; }} /> </ScrollView> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, }, scrollContainer: { flex: 1, paddingHorizontal: 15, }, scrollContentContainer: { paddingTop: 40, paddingBottom: 10, }, }); const pickerSelectStyles = StyleSheet.create({ inputIOS: { fontSize: 16, paddingVertical: 12, paddingHorizontal: 10, borderWidth: 1, borderColor: 'gray', borderRadius: 4, color: 'black', paddingRight: 30, // to ensure the text is never behind the icon }, inputAndroid: { fontSize: 16, paddingHorizontal: 10, paddingVertical: 8, borderWidth: 0.5, borderColor: 'purple', borderRadius: 8, color: 'black', paddingRight: 30, // to ensure the text is never behind the icon }, });
Coming Soon…
Quick Links
Legal Stuff