How to create custom dropdown list in react-native? - javascript

I almost consume 17 hours, but the answers on what I have searched did not satisfy the exact answer to my question,
how to create own custom DropdownList in react native? I already did, the 1st child node which is DropDown (cyan) is work properly, the 2nd child node which is the the List (green) with absolute position but it was not work properly, unless the root parent (red) will also covered the 2nd child node with absolute position, to make it active and clickable, as like what I mention from the picture, Figure 1.0
I set absolute position of the List (green) to make it float from the next child node and the error was persist, the List(parent) was disabled and it cannot click each child items (yellow), unless I set height of the root parent (red) that covered the entire child node but it leads to the next node to adjust the position and do the unexpected layout, like the sample picture Figure 1.1

I DID IT!, the problem is the root parent (red) must not have customized styled property, and I still have one questions, I dont know if it is bug or what, once I did not put any custom property in the style of root parent it works properly, and here is my source code
App.js
import React, { useState } from 'react';
import Separator from './Separator';
import Spinner from './Spinner';
import {
View,
Text,
TouchableOpacity
} from 'react-native';
export default function () {
const [spBusiness, setSPBusiness] = useState({
showList: false,
selectedItem: ''
})
const onShowList = () => {
if (spBusiness.showList) {
setSPBusiness(prev => ({
...prev,
showList: false
}))
}
else {
setSPBusiness(prev => ({
...prev,
showList: true
}))
}
}
const onSelectItem = (item, index) => {
setSPBusiness(prev => ({
showList: false,
selectedItem: item
}));
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff' }}>
<Box text='Rectangle 1' onPress={() => console.log('Rect 1')} />
<Separator vertical={1} />
<Spinner
placeholder='Select Item'
items={['A', 'B', 'C']}
isListShow={spBusiness.showList}
selectedItem={spBusiness.selectedItem}
onShowList={onShowList}
onSelectItem={onSelectItem}
/>
<Separator vertical={1} />
<Box text='Rectangle 2' onPress={() => console.log('Rect 2')} />
<Separator vertical={1} />
</View>
)
}
const Box = props => (
<TouchableOpacity onPress={props.onPress} style={{ zIndex: -1, borderRadius: 7.5, width: 250, height: 45, justifyContent: 'center', alignItems: 'center', backgroundColor: '#242424' }}>
<Text style={{ color: '#fff' }}>{props.text}</Text>
</TouchableOpacity>
)
DropDownList/index.js
import React from 'react';
import styles from './style';
import AIcon from 'react-native-vector-icons/AntDesign';
import {
View,
Text,
Dimensions,
TouchableOpacity,
ScrollView
} from 'react-native';
import Separator from '../Separator';
export default function (props) {
const { items } = props;
const { width, height } = Dimensions.get('window');
const size = size => (size / 100) * width;
const hp = hp => (hp / 100) * height;
const wp = wp => (wp / 100) * width;
return (
<View>
{/* here is the root parent with empty style property*/}
<TouchableOpacity
style={styles.skin}
activeOpacity={0.75}
onPress={props.onShowList}
>
<View style={styles.body}>
<View style={styles.upperBodyPane}>
</View>
<View style={styles.centerBodyPane}>
{props.selectedItem.length === 0 &&
<Text>{props.placeholder}</Text>
}
{props.selectedItem.length !== 0 &&
<Text>{props.selectedItem}</Text>
}
<AIcon name='caretdown' size={size(3.5)} color='#242424' />
</View>
</View>
</TouchableOpacity>
{props.isListShow &&
<ScrollView
style={styles.list}
contentContainerStyle={styles.listContainer}
>
{
items.map((item, index) => (
<View key={index}>
<Item
item={item}
onPress={() => props.onSelectItem(item, index)}
/>
{items.length !== (index + 1) ? <Separator vertical={0.5} /> : null}
</View>
))
}
</ScrollView>
}
</View>
)
}
const Item = props => (
<TouchableOpacity style={styles.item} onPress={props.onPress}>
<Text style={styles.itemText}>{props.item}</Text>
</TouchableOpacity>
)
DropDownList/style.js
import {
Dimensions,
StyleSheet
} from 'react-native';
const { width, height } = Dimensions.get('window');
const size = size => (size / 100) * width;
const hp = hp => (hp / 100) * height;
const wp = wp => (wp / 100) * width;
export default StyleSheet.create({
skin: {
minWidth: wp(70),
height: hp(5.5),
paddingHorizontal: wp(6),
justifyContent: 'center',
backgroundColor: 'cyan',
position: 'relative'
},
body: {
},
centerBodyPane: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
},
item: {
height: hp(5),
paddingHorizontal: wp(5),
justifyContent: 'center',
backgroundColor: 'orange',
},
itemText: {
color: 'white'
},
list: {
top: hp(6.5),
width: wp(70),
maxHeight: hp(15),
backgroundColor: 'green',
position: 'absolute',
},
listContainer: {
padding: size(1.75),
}
})
but the worse case only, I need to set the next child node of DropDownList to zIndex: -1 , but it finally works.

Related

How to move label along with slider value in React Native

I am working on a React Native Project, I want to move the label along with the slider value. I tried many ways but its not working accurately.
Here is my code:
import React, { useState } from 'react'
import { Text, StyleSheet, View, Dimensions } from 'react-native'
import Slider from '#react-native-community/slider';
function LabelSlider() {
const windowWidth = Dimensions.get('window').width;
const [value, setValue] = useState(0)
// const left = value * (windowWidth-60)/85;
const [showValue, setShowValue] = useState(false)
//var percent = (percentToGet / 100) * number;
return (
<View>
{showValue ?
<View >
<Text style={{ width:50, textAlign: 'auto', left: value}}>{value}</Text>
</View> :
<></>}
<Slider
style={{ width: 200, height: 40 }}
maximumValue={300}
minimumValue={0}
step={1}
value={value}
onValueChange={(value) => setValue(value)}
onSlidingStart={() => setShowValue(true)}
onSlidingComplete={() => setShowValue(true)}
minimumTrackTintColor="#FFFFFF"
maximumTrackTintColor="#000000"
/>
</View>
)
}
export { LabelSlider }
I have also attached two images of the output I was able to get:
Image 1,
Image 2
You can use a shared value and interpolate the left position for the label.
Right now what happens is, that the left value goes from 0 to 300. This makes the label unresponsive. You can do something as shown below.
Also, here's a Snack for the implementation.
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Slider from '#react-native-community/slider';
import Animated, {
useSharedValue,
useAnimatedStyle,
interpolate,
Extrapolate,
} from 'react-native-reanimated';
// Constants
const MAX_SLIDER_VALUE = 300;
const MIN_SLIDER_VALUE = 0;
const SLIDER_WIDTH = 200;
export default function App() {
// State to store progress value of slider
const [value, setValue] = React.useState(0);
// Shared value for storing label position
const progress = useSharedValue(0);
// Whenever `value` changes, update the progress shared value
// This will accordingly update the left position of the label
React.useEffect(() => {
progress.value = value;
}, [value]);
// Animate the left position according to the shared value
// Also Extrapolate it so that it doesn't exceed 100%
const animatedStyle = useAnimatedStyle(() => {
const leftPosition = interpolate(
progress.value,
[MIN_SLIDER_VALUE, MAX_SLIDER_VALUE],
[0, 100],
Extrapolate.CLAMP
);
return {
left: `${leftPosition}%`,
};
});
return (
<View style={styles.container}>
<View style={{ width: SLIDER_WIDTH }}>
<Animated.View style={[styles.labelView, animatedStyle]}>
<Text>{value}</Text>
</Animated.View>
<Slider
style={{ width: SLIDER_WIDTH, height: 40 }}
maximumValue={MAX_SLIDER_VALUE}
minimumValue={MIN_SLIDER_VALUE}
step={1}
value={value}
onValueChange={setValue}
minimumTrackTintColor="#FFFFFF"
maximumTrackTintColor="#000000"
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
labelView: {
marginBottom: 20,
},
});
You can use react native element slider for this, I have given the example code below
import React from 'react';
import { View } from 'react-native';
import {Slider} from 'react-native-elements';
const App = () => {
return (
<View
style={{
flex: 1,
justifyContent: "center",
}}>
<Slider
thumbStyle={{height: 15, width: 15, backgroundColor: 'orange'}}
maximumTrackTintColor="grey"
minimumTrackTintColor="orange"
thumbProps={{
children: (
<View
style={{
color: 'green',
marginTop: -22,
width: 100,
}}>
<Text>Value</Text>
</View>
),
}}
/>
</View>
)
}
export default App;

React Native Flip Card not Working on flip

I am trying to make a flip card game. I used GestureFlipView for flip card animation. I want to display these flip card in 3X3 grid and for that I have used from react native. But the problem occurs that cards are not getting flipped and it is showing vague behaviour as well. Just the last card working fine and other cards are doing unpredictable behaviour.
Github Repo: https://github.com/akhilomar/React-Native-Number-Game
CardScreen: https://i.stack.imgur.com/Cliww.png
Card Component
import {View, Text, SafeAreaView, TouchableOpacity} from 'react-native';
import GestureFlipView from 'react-native-gesture-flip-card';
const Cards = (props) => {
const [flipType, setFlip] = useState('left');
useEffect(() => {
})
const renderFront = () => {
return(
<TouchableOpacity onPress = {() => {
this.flipView.flipRight()
setFlip('right');
console.log("Pressed" + `${props.val}`)
}} >
<View style = {{backgroundColor:'red', width: 100, height: 100, alignItems: 'center', justifyContent: 'center'}}>
<Text style = {{color: "white", fontSize: 20}}>Swipe Me</Text>
</View>
</TouchableOpacity>
);
};
const renderBack = () => {
return(
<View style = {{backgroundColor:'blue', width: 100, height: 100, alignItems: 'center', justifyContent: 'center'}}>
<Text style = {{color: "white", fontSize: 30}}>{props.val}</Text>
{/* <TouchableOpacity onPress = {() => {
(flipType === 'left') ? this.flipView.flipRight() : this.flipView.flipLeft();
setFlip((flipType === 'left') ? 'right' : 'left');
}} style = {{padding: 10, backgroundColor: 'purple', width: 100, height: 40, alignItems: 'center', justifyContent: 'center'}}>
<Text style = {{color: 'white'}}>Reverse</Text>
</TouchableOpacity> */}
</View>
);
};
//ref = {(ref) => this.flipView = ref}
return(
<SafeAreaView style = {{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<GestureFlipView ref = {(ref) => this.flipView = ref} width={300} height = {500}>
{renderFront()}
{renderBack()}
</GestureFlipView>
</SafeAreaView>
);
}
export default Cards;```
**Card List Component**
```import React from 'react';
import {SafeAreaView, View, FlatList, Dimensions, StyleSheet } from 'react-native';
import Cards from './Cards';
const CardScreen = () => {
// const data = ['1','2','3','4','5','6','7','8','9'];
const DATA = [
{
id: '1',
title: '1',
},
{
id: '2',
title: '2',
},
{
id: '3',
title: '3',
},
{
id: '4',
title: '4',
},
{
id: '5',
title: '5',
},
{
id: '6',
title: '6',
},
{
id: '7',
title: '7',
},
{
id: '8',
title: '8',
},
{
id: '9',
title: '9',
}
];
const Shuffle = (arr1) => {
var ctr = arr1.length, temp, index;
while (ctr > 0) {
index = Math.floor(Math.random() * ctr);
ctr--;
temp = arr1[ctr];
arr1[ctr] = arr1[index];
arr1[index] = temp;
}
return arr1;
}
const numColumns = 3;
const size = Dimensions.get('window').width/numColumns;
const styles = StyleSheet.create({
itemContainer: {
width: size,
height: size,
},
item: {
flex: 1,
margin: 3,
backgroundColor: 'lightblue',
}
});
return(
<>
<FlatList
data={DATA}
renderItem={({ item }) => (
<View style={styles.itemContainer}>
<Cards val = {item.value}/>
</View>
)}
keyExtractor={item => item.id}
numColumns={numColumns} />
{/* {
data.map((index, item) => {
return(
<View style={styles.itemContainer}>
<Cards val = {item}/>
</View>
);
})
} */}
</>
);
}
export default CardScreen;```
You need to use ref correctly. you can Read about it here
const Cards = (props) => {
//first define ref
const flipViewRef = React.useRef();
//in onPress use it like this
<TouchableOpacity onPress = {() => {
flipViewRef.current.flipRight()
...
}} >
//in GestureFlipView assign it like this
<GestureFlipView ref={flipViewRef} />
}
The primary cause of your troubles is the fact that you are using a this reference within a functional component. As explained here, the value of this will be determined by how the function is called, and might even be undefined. A more reliable approach of using this is from a class context. For React, that means using a class component, rather than a functional component, which is what is being used here. You can read about function and class components here.
Something else to consider is if a FlatList is appropriate here. Typically, this component is used to improve performance for rendering large lists. Instead of using a FlatList, I would recommend using something simpler, such as a set of View components to draw the cards. Here is a complete example based on your code:
import React, { useState } from 'react';
import { View, Dimensions, StyleSheet, Text, TouchableOpacity } from 'react-native';
import GestureFlipView from 'react-native-gesture-flip-card';
const Card = (props: any) => {
const [flipType, setFlip] = useState('left');
let flipView: any;
const onFrontPress = () => {
flipView.flipRight()
setFlip('right');
}
const cardDimensions = { width: 0.9 * props.size, height: 0.9 * props.size };
const renderFront = () => {
return (
<TouchableOpacity onPress={onFrontPress} style={[styles.front, cardDimensions]}>
<Text style={styles.frontText}>Swipe Me</Text>
</TouchableOpacity>
);
};
const renderBack = () => {
return (
<View style={[styles.back, cardDimensions]}>
<Text style={styles.backText}>{props.val}</Text>
</View>
);
};
return (
<GestureFlipView ref={(ref) => flipView = ref} width={props.size} height={props.size}>
{renderFront()}
{renderBack()}
</GestureFlipView>
);
}
const CardRow = () => {
const size = Dimensions.get('window').width / 3;
return (
<View style={styles.rowContainer}>
<Card size={size} />
<Card size={size} /{ width: 0.9 * props.size, height: 0.9 * props.size }>
<Card size={size} />
</View>
);
}
const CardScreen = () => {
return (
<View style={styles.container}>
<CardRow />
<CardRow />
<CardRow />
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
flex: 1,
},
rowContainer: {
flexDirection: 'row',
justifyContent: 'space-evenly',
},
back: {
backgroundColor: 'blue',
alignItems: 'center',
justifyContent: 'center'
},
backText: {
color: "white",
fontSize: 30
},
front: {
backgroundColor: 'green',
alignItems: 'center',
justifyContent: 'center',
},
frontText: {
color: "white",
fontSize: 20
}
});
export default CardScreen;

Interpolate is not a function react native

I follow video at the link https://www.youtube.com/watch?v=YE7c6ch2msY and try do it.
But when I add animated interpolate it show error " interpolate is not a function"
Here is my code
import * as React from 'react';
import { StatusBar, Animated, Text, Image, View, StyleSheet, Dimensions, FlatList } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
const { width, height } = Dimensions.get('screen');
const Indicator = ({ scrollX }) => {
return (
<View style={{ position: 'absolute', bottom: 100, flexDirection: 'row' }}>
{DATA.map((_, i) => {
const inputRange =[(i-1) * width, i* width, (i+1)* width];
const scale = scrollX.interpolate({
inputRange,
outputRange:[0.8,1.4,0.8],
extrapolate:'clamp',
})
return <View key={`indicatot-${i}`}
style={{
height: 10,
width: 10,
borderRadius: 5,
backgroundColor: '#333',
margin: 10
}} />
})}
</View>
)
}
export default function App() {
const scrollX = React.useRef(new Animated.Value(0)).current;
return (
<View style={styles.container}>
<StatusBar hidden />
<Animated.FlatList
...
renderItem={({ item }) => {
return (
...
)
}}>
</Animated.FlatList>
<Indicator scrollX={{ scrollX }} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
I can see type of scrollX is any, is not Animted.Value
How I can fix it?
It's just a little typo, you're putting double brackets when passing down the scrollX
<Indicator scrollX={{ scrollX }} />
This means you're passing an object with a property scrollX, just remove one set of brackets scrollX={ scrollX }

REACT NATIVE: Scroll View keep bouncing back up to the top doesn't scroll

I am trying to get my ScrollView to scroll down so I can see all my views but it keeps bouncing back up to the top.
What it looks like
The White panels below Physiological Classification are in the ScrollView and are supposed to scroll but it just bounces back to the top.
Here is where my ScrollView is
_renderContent = (section) => {
section.arrow = !(section.arrow);
return (
<ScrollView>
<QuestionPanels
Questions={section.Questions}
pickerDefaultValues={section.pickerDefaultValues}
pickerItemNames={section.pickerItemNames}
/>
</ScrollView>
);
};
Question Panels is just a component that generates Views based on a json file. If relevant here is the code for that as well.
import React, { Component, useState, setState} from 'react';
import { ScrollView, View, Text } from 'react-native'
import styles from '../../Styles/GeneralStyles';
import Picker from '../Picker/Picker'
import appData from '../../DataSheet/appData.json';
const Panel = (props) => {
return (
<View style={styles.dropDownCardPanel}>
<View style={styles.QuestionPanel}>
<Text style={styles.QuestionText}>{props.Question}</Text>
</View>
<View style={styles.AnswerPanel}>
<Picker
defaultVal= {props.pickerDefaultValues}
showButton={false}
pickerItemNames={props.pickerItemNames}
/>
</View>
</View>
);
}
export default class QuestionairePanels extends Component {
constructor(props){
super(props)
}
render() {
var panelList = [];
for(let i = 0; i < this.props.Questions.length; i++){
panelList.push(
<Panel
key={i}
Question={this.props.Questions[i]}
pickerDefaultValues ={this.props.pickerDefaultValues[i]}
pickerItemNames = {this.props.pickerItemNames[i]}
/>
);
}
return(
<View>
{panelList}
</View>
);
}
}
Here are the styles that matter for this
dropDownCardPanel: {
height: 200,
flexDirection: "row",
backgroundColor: '#faf2f2',
paddingTop: 5,
paddingBottom: 5,
borderBottomWidth: 1,
borderColor: 'black',
},
dropDownCardPanelText: {
fontSize: 15,
color: "black",
marginLeft: 10
},
icon: {
left: 20,
},
QuestionPanel: {
height: "100%",
width: "60%",
borderRightWidth: 1,
borderColor: 'black',
left: 3,
marginRight: 5,
alignItems: "center",
justifyContent:'center'
},
QuestionText: {
fontSize: 20,
},
AnswerPanel: {
height: "100%",
width: "40%",
right: 3
}
The reason why I was not able to scroll was that the tag needs a specific height for it to render for a scroll. So to fix this I had to put a view around the and set it to a specific height.
Corrected Code
section.arrow = !(section.arrow);
var scrollHeight = (section.Questions.length > 2) ? 600 : 400;
var headerDisplay = (section.name.match("Physiologic")) ? section.name + " Variables" : "Select Dominant Final Diagnosis";
return (
<View style={{height: scrollHeight}}>
<ScrollView>
<View style={styles.dropDownCardPanelHeader}>
<Text style={styles.dropDownCardPanelHeaderText}>
{headerDisplay}
</Text>
</View>
<QuestionPanels
Questions={section.Questions}
pickerItemNames={section.pickerItemNames}
/>
</ScrollView>
</View>
Later I will try to use Flex to set the height so the react native project can scale to all screens but so far it works for iOS

Styling reusable components in React-Native

I made a reusable component Button.js and I'm importing it on two different screens. The button looks the same, but on the first screen I need it to be position: 'absolute' and on the second one position: 'relative' (the default).
How do I add the position to be absolute on the first screen? I tried to add the styling on FirstPage.js but it does not work. How do I overwrite the style that is defined in Button.js?
Button.js:
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
return (
<TouchableOpacity onPress={onPress} style={buttonStyle, {'position': position}}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
You can pass props, something like this (Button.js) (edited according to posted code):
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ position, onPress, children }) => {
const { buttonStyle, textStyle } = styles;
const style = {...buttonStyle, position };
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={textStyle}>{children}</Text>
</TouchableOpacity>
);
};
Button.defaultProps = {
position: 'relative',
}
const styles = {
textStyle: {
alignSelf: 'center',
color: '#F44336',
fontSize: 16,
},
buttonStyle: {
zIndex: 2,
width: 100,
backgroundColor: '#FFF',
}
};
export { Button };
Your button of course looks different, this is just an outline of what you could do (basically just using props).
This is REUSABLE Button as touchableOpacity.
export const NormalThemeButton = (props) => {
return (
<TouchableOpacity
style={[{ ...props.style, ...styles.normalThemeBtn }]}
style={[{ alignItems: props.align }, styles.anchor]}
onPress={props.onPress} >
<CustomText text={props.text} l bold style={{
textAlign: 'center',
color: theme['blackColor']
}} />
{props.children}
</TouchableOpacity >
);
};
This is where this RESUABLE Button is been used.
style={{ ...styles.openArrivedButton }}
text={Lng.instance.translate('useWaze')}
onPress={() => { Waze() }}/>
Hope You find it helpful.

Categories