I can only use react native in the project, I need to make a Toggle Component with AnimatedView. I tried with react native switcher but it won't be responsive for mobile and web at the same time.
Here is my code
export const ToggleButton = () => {
const [isEnabled, setIsEnabled] = useState(false);
const [text, setText] = useState('');
const toggleSwitch = () => {
if (isEnabled) {
setText('OFF');
} else {
setText('ON');
}
setIsEnabled(previousState => !previousState);
};
return (
<View style={styles.container}>
<View>
{isEnabled ? <Text style={styles.textOn}>On</Text> : <Text style={styles.textOff}>Off</Text>}
<Switch
trackColor={{ false: Colors.BlueLight, true: Colors.PurpleLight }}
thumbColor={isEnabled ? Colors.BlueLight : Colors.BlueLight}
ios_backgroundColor="#3E3E3E"
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
</View>
);
};
Someone give me a recommendation how to do it?
Hye finally i made a custom switch, do check out :
Do check out this expo https://snack.expo.dev/#gaurav1995/gnarly-sandwich
Its completely built with react native, no external libraries etc
Do lemme know in case of any doubts :)
import React, { useState, useRef } from 'react';
import {
Text,
View,
StyleSheet,
Animated,
TouchableOpacity,
Easing
} from 'react-native';
export default function App() {
const positionButton = useRef(new Animated.Value(0)).current;
const [isOn, setIsOn] = useState(false);
const startAnimToOff = () => {
Animated.timing(positionButton,{
toValue:0,
duration:500,
easing:Easing.ease
}).start()
};
const startAnimToOn = () => {
Animated.timing(positionButton,{
toValue:1,
duration:500,
easing:Easing.ease
}).start()
};
const positionInterPol = positionButton.interpolate({inputRange:[0,1],outputRange:[0,30]})
const backgroundColorAnim = positionButton.interpolate({inputRange:[0,1],outputRange:["#767577","#81b0ff"]})
const initialOpacityOn = positionButton.interpolate({inputRange:[0,1],outputRange:[0,1]})
const initialOpacityOff = positionButton.interpolate({inputRange:[0,1],outputRange:[1,0]})
const onPress = () => {
if (isOn) {
startAnimToOff();
setIsOn(false);
} else {
startAnimToOn();
setIsOn(true);
}
};
return (
<View style={styles.container}>
<TouchableOpacity style={{height:30,width:60}} activeOpacity={0.9} onPress={onPress} >
<Animated.View style={[styles.mainStyes,{
backgroundColor:backgroundColorAnim
}]} >
<Animated.Text
style={[
styles.eahcStyles,
{
opacity: initialOpacityOn,
},
]}>
ON
</Animated.Text>
<Animated.Text
style={[
styles.eahcStylesOf,
{
opacity: initialOpacityOff,
},
]}>
OFF
</Animated.Text>
<Animated.View style={[styles.basicStyle,{
transform:[{
translateX:positionInterPol
}]
}]} />
</Animated.View>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
basicStyle: {
height: 20,
width: 20,
borderRadius: 20,
backgroundColor: '#FFF',
marginTop: 5,
marginLeft: 5,
},
eahcStyles: {
fontSize: 14,
color: '#f5dd4b',
position: 'absolute',
top: 6,
left: 5,
},
eahcStylesOf: {
fontSize: 14,
color: '#f4f3f4',
position: 'absolute',
top: 6,
right: 5,
},
mainStyes: {
borderRadius: 30,
backgroundColor: '#81b0ff',
height: 30,
width: 60,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
Related
I have a problem with Login Screen I am using Formik to use custom TextInputs but I am getting this error: (Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.)
This is my LoginScreen.js
import React, {useState} from 'react';
import { Alert,
Image,
KeyboardAvoidingView,
ScrollView,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native';
import AppText from '../../components/AppText';
import AppButton from '../../components/AppButton';
import colors from '../../configs/colors';
import AppInput from '../../components/AppInput';
import {Formik} from 'formik';
import * as Yup from 'yup';
import http from '../../services/http';
import storage from '../../services/storage';
import {setUser} from '../../store/reducers/auth';
import {CommonActions} from '#react-navigation/native';
import {useDispatch} from 'react-redux';
import ProgressDialog from 'react-native-progress-dialog';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
const LoginScreen = ({navigation}) => {
const dispatch = useDispatch();
const [loading, setLoading] = useState(false);
const loginUser = async values => {
try {
setLoading(true);
values['role'] = 2;
values['device_token'] ='XYZ';
const res = await http.post({
url: '/user/login',
body: values,
});
console.log('Res => ', res.data);
if (res.data.records.isVerified === 1) {
dispatch(setUser(res.data));
http.setUserTokenAndId(
res.data.records.accessToken,
res.data.records.id,
);
await storage.store('USER', res.data);
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{name: 'Home'}],
}),
);
} else {
Alert.alert('Error', res.data.message, [
{
text: 'Resend Email',
onPress: () => {
resendEmail(res.data.records.accessToken);
},
style: 'destructive',
},
{
text: 'Cancel',
onPress: () => {},
style: 'cancel',
},
]);
}
} catch (err) {
console.log('Err => ', err);
} finally {
setLoading(false);
}
};
const resendEmail = async token => {
try {
setLoading(true);
const res = await http.post({
url: '/email/verification-notification',
body: {},
headers: {Authorization: 'Bearer ' + token},
});
Alert.alert('Success', res.data.message);
} catch (err) {
console.log('Err', err);
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<View style={styles.cardContainer}>
<View style={styles.card}>
<View>
<KeyboardAwareScrollView style={{height: '100%'}}>
<>
<Image source={require('../../assets/header.png')} />
<AppText style={styles.headingText}>Sign In To Forsa</AppText>
<AppText style={styles.subHeadingText}>
Welcome Back! sign in to your previous account to continue the
shoping
</AppText>
</>
<View>
<Formik
initialValues={{email: '', password: ''}}
validationSchema={Yup.object({
email: Yup.string().label('Email').required(),
password: Yup.string().label('Password').min(5).required(),
})}
onSubmit={loginUser}>
{({submitForm}) => {
return (
<View>
<Image
style={{
alignItems: 'center',
marginTop: 10,
height: 150,
width: '100%',
}}
resizeMode="contain"
source={require('../../assets/loginicon.png')}
/>
<View style={{marginHorizontal: 20}}>
<AppInput
label=""
placeholder="Email*"
keyboardType="email-address"
name="email"
iconName="email"
autoComplete={true}
inputStyle={styles.input}
inputWrapperStyle={styles.inputWrapper}
/>
<AppInput
label=""
placeholder="Password*"
name="password"
secureTextEntry={true}
iconName="lock"
inputWrapperStyle={styles.inputWrapper}
inputStyle={styles.input}
/>
</View>
<AppButton
containerStyle={styles.loginBtn}
onPress={submitForm}
title={'Login'}></AppButton>
<View
style={{
justifyContent: 'space-between',
flexDirection: 'row',
marginTop: 10,
}}>
<View />
<AppText
style={{
color: colors.TAB_BAR_GREY,
fontSize: 13,
marginRight: 25,
}}
onPress={() =>
navigation.navigate('forgotPassword')
}>
Forgot Password?
</AppText>
</View>
<View
style={{
justifyContent: 'center',
flexDirection: 'row',
marginTop: 10,
}}>
<AppText>Don't have any account yet?</AppText>
<TouchableOpacity
onPress={() =>
navigation.navigate('CreateAccount')
}>
<AppText
style={{
marginLeft: 10,
color: colors.PRIMARY_GREEN,
}}>
Sign Up
</AppText>
</TouchableOpacity>
</View>
</View>
);
}}
</Formik>
</View>
</KeyboardAwareScrollView>
</View>
{loading && (
<ProgressDialog
loaderColor={colors.PRIMARY_GREEN}
visible={loading}
labelStyle={{marginTop: 10}}
/>
)}
</View>
</View>
</View>
);
};
export default LoginScreen;
const styles = StyleSheet.create({
headingText: {
fontSize: 30,
position: 'absolute',
marginTop: 80,
marginLeft: 20,
color: colors.WHITE,
fontWeight: '600',
},
subHeadingText: {
fontSize: 18,
position: 'absolute',
marginTop: 120,
marginLeft: 20,
color: colors.WHITE,
fontWeight: '400',
},
loginBtn: {
marginTop: 20,
borderRadius: 50,
marginHorizontal: 20,
},
input: {
paddingLeft: 10,
},
inputWrapper: {
borderWidth: 0.5,
borderRadius: 50,
},
label: {
color: colors.PRIMARY_GREEN,
},
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
// backgroundColor: "rgba(66,66,66,0.47)",
},
btnText: {
color: colors.WHITE,
fontSize: 16,
},
});
This is my AppInput.js (Which is custom TextInput):
import React from 'react';
import {View, TextInput, StyleSheet, Platform} from 'react-native';
import AppText from './AppText';
import {useFormikContext} from 'formik';
import colors from '../configs/colors';
import {MaterialCommunityIcons} from 'react-native-vector-
icons/MaterialCommunityIcons';
export default props => {
const {
name,
label,
labelStyle,
inputWrapperStyle,
inputWrapperErrorStyle,
inputStyle,
errorStyle,
containerStyle,
iconName,
value,
multiline = false,
editable = true,
} = props;
const {values, errors, touched, setFieldValue} = useFormikContext();
let wrapperErrorStyle = error
? {
borderWidth: 1,
borderColor: colors.ERROR_TEXT,
...inputWrapperErrorStyle,
}
: {
// borderWidth: 1,
// borderColor: props.value ? colors.LIGHT_GREY : colors.LIGHT_GREY,
...inputWrapperErrorStyle,
};
const error = touched[name] && errors[name];
let iconColor = props.value ? colors.PRIMARY_GREEN : colors.TAB_BAR_GREY;
if (error) iconColor = colors.errorText;
return (
<View style={[styles.containerStyle, containerStyle]}>
<AppText style={[styles.label, labelStyle]}>{label}</AppText>
<View style={[styles.inputWrapper, inputWrapperStyle, wrapperErrorStyle]}>
{iconName && (
<MaterialCommunityIcons
name={iconName}
size={25}
style={styles.iconStyles}
color={iconColor}
/>
)}
<TextInput
{...props}
value={value}
editable={editable}
onChangeText={txt => {
setFieldValue(name, txt);
}}
multiline={multiline}
placeholderTextColor={colors.TAB_BAR_GREY}
style={[styles.input, inputStyle]}
/>
</View>
{error ? (
<AppText style={[styles.error, errorStyle]}>{error}</AppText>
) : null}
</View>
);
};
const styles = StyleSheet.create({
containerStyle: {
marginTop: Platform.OS === 'ios' ? 5 : 0,
marginBottom: 0,
},
label: {
fontSize: 15,
},
inputWrapper: {
flexDirection: 'row',
borderWidth: 0,
},
iconStyles: {
justifyContent: 'center',
alignItems: 'center',
marginTop: 7,
marginLeft: 18,
},
input: {
flex: 1,
minHeight: 40,
maxHeight: 150,
fontSize: 16,
color: colors.BLACK,
},
error: {
color: colors.ERROR_TEXT,
marginTop: 3,
fontSize: 16,
},
});
I am searching from 3-4 hours but couldn't find any solution to this, I am kinda new to react native. Any help will be appreciated. Thank you.
import React from 'react';
import {
Text,
StyleSheet,
View,
ActivityIndicator,
TextInput,
Alert,
} from 'react-native';
import {RightOfHeader} from '../assets/components/rightOfHeader';
import Pdf from 'react-native-pdf';
import TeekaPDF from '../assets/components/teekaPdf';
import {useSelector, useDispatch} from 'react-redux';
import {setAngNum} from '../redux/actions';
import {allColors} from '../assets/styleForEachOption';
export default function OpenPdf({navigation, route}) {
const [totalAngs, setTotalAngs] = React.useState(0);
const [currentAng, setCurrentAng] = React.useState(1);
const [showHeader, setShowHeader] = React.useState(true);
const currentAngRef = React.useRef(1); //only for addListner
const state = useSelector(theState => theState.theReducer);
const dispatch = useDispatch();
const {pdfTitle} = route.params;
const fileName = pdfTitle.split(' ').join(''); //replaces " " with ""
const sourceFileName = {uri: state.allPdfs[pdfTitle].uri};
React.useEffect(() => {
//pick up where you left off from last time
//this.pdf.setPage(state.allPdfs[pdfTitle].currentAng);
navigation.addListener('beforeRemove', () => {
dispatch(setAngNum(pdfTitle, currentAngRef.current));
});
}, [totalAngs, navigation]);
const headerStyles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'space-evenly',
},
title: {
flex: 1,
margin: 10,
borderRadius: 5,
height: '75%',
backgroundColor: '#077b8a',
justifyContent: 'center',
textAlign: 'center',
},
pagesBox: {
flex: 1.5,
margin: 10,
borderRadius: 5,
alignItems: 'center',
justifyContent: 'center',
// justifyContent: 'space-evenly',
fontSize: 20,
flexDirection: 'row',
backgroundColor: '#077b8a',
},
setAngNumBox: {
borderRadius: 5,
height: '95%',
fontSize: 20,
backgroundColor: '#a2d5c6',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
},
boxText: {
fontSize: 25,
},
headerBtnsCont: {
flex: 1,
flexDirection: 'row',
},
headerBtns: {
flex: 1,
paddingTop: 20,
},
});
React.useEffect(() => {
let showTitle = pdfTitle;
if (showTitle.length > 15) showTitle = showTitle.slice(0, 15) + '...';
console.log("YOYOYOO")
navigation.setOptions({
headerShown: showHeader,
headerTintColor: state.darkMode ? 'white' : 'black',
headerStyle: {
backgroundColor: allColors[state.darkMode].headerColor,
},
headerTitle: () => {
return (
<View style={headerStyles.container}>
<Text style={headerStyles.title}>{showTitle}</Text>
<View style={headerStyles.pagesBox}>
<TextInput
keyboardType="numeric"
value={currentAng.toString()}
style={headerStyles.setAngNumBox}
placeholder="ex: 5"
onChangeText={text => {
if (text.length === 0) {
setCurrentAng('');
} else if (text.length < 5) {
setCurrentAng(parseInt(text));
}
}}
onSubmitEditing={e => {
const asInt = currentAng;
if (asInt) {
//this.pdf.setPage(asInt);
if (asInt > totalAngs) {
setCurrentAng(totalAngs);
}
}
}}
/>
<Text style={headerStyles.boxText}>
/{totalAngs}
</Text>
</View>
</View>
);
},
});
});
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: allColors[state.darkMode].mainBackgroundColor,
},
pdf: {
width: '100%',
height: '99%',
borderRadius: 15,
},
});
return (
<View style={styles.container}>
<Pdf
ref={pdf => {
//this.pdf = pdf;
}}
activityIndicator={
<ActivityIndicator size="large" color="blue" />
}
source={sourceFileName}
onLoadComplete={(numberOfPages, filePath) => {
setTotalAngs(numberOfPages);
}}
onPageChanged={(page, numberOfPages) => {
if (
showHeader &&
state.hideHeaderOnScroll &&
page > currentAng
) {
setShowHeader(false);
}
setCurrentAng(page);
}}
onPageSingleTap={() => {
setShowHeader(!showHeader);
}}
onError={error => {
Alert.alert(
'PDF ERROR',
String(error),
[
{
text: 'Cancel',
style: 'cancel',
},
],
{
cancelable: true,
},
);
}}
onPressLink={uri => {
console.log(`Link presse: ${uri}`);
}}
style={styles.pdf}
/>
</View>
);
}
The problem I'm receiving is when the page changes, it goes to the 'onPageChanged' function and in the function I have a setter hook setCurrentAng(page). This changes the current page number for the user in headerTitle. It has no effect on the pdf. I have a variable CurrentAng which shows the user the current page number of the pdf. SetCurrentAng changes the CurrentAng to show the current page number. When I scroll to a new page(when pageChanged), It is very choppy and not smooth. This is because when the page is changed, I set the current page number which makes everything rerender again and making it choppy. when I comment 'setCurrentAng(page);' in onPageChanged function, the pdf scrolls smoothly but then i am not able to see which page number I am on. Please forgive me for my lack of knowladge and mistakes. Thanks for reading this. Hope you can help
So I originally needed to pass states while navigating from a screen to another because I thought that would be enough to update a button as well as the screen iteself. However, the button that controlled the states is not being updated with the screen.
In the demo provided below(I included the code here as well) you can see that when navigating from Screen3, the state updates so that the red screen renders but the button at the top does not update as well.
How can I update the button along with the screen being updated?
I need to go from screen3 to the red screen while the button at the top shows we are on the red screen as well.
Here is the demo as well as the code below. Please keep in mind you must run the snack on IOS or android and you need to be running Expo Version 42(located in the bottom right of the screen)
Thank you for any insight at all! I appreciate it more than you know.
Home.js
import Slider from './components/slider'
const Home = ({ route }) => {
const [isVisile, setIsVisible] = React.useState(true);
const [whichComponentToShow, setComponentToShow] = React.useState("Screen1");
React.useEffect(() => {
if(route.params && route.params.componentToShow) {
setComponentToShow(route.params.componentToShow);
}
}, [route.params]);
const goToMap = () => {
setComponentToShow("Screen2");
}
const goToList = () => {
setComponentToShow("Screen1");
}
return(
<View style={{backgroundColor: '#d1cfcf' ,flex: 1}}>
{whichComponentToShow === 'Screen1' && <ListHome />}
{whichComponentToShow === 'Screen2' && <MapHome />}
<View style={{position: 'absolute', top: 0, left: 0, right: 1}}>
<Slider
renderMap={goToMap}
renderList={goToList}
/>
</View>
</View>
);
}
Screen3.js
const Screen3 = (props) => {
const navigation = useNavigation();
const onPress = () => {
navigation.navigate('Home', {
screen: 'Home',
params: {
componentToShow: 'Screen2'
}
});
}
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity onPress={onPress}>
<Text style={{ color: 'black', fontSize: 25 }}>
Navigate to home and change to map screen
</Text>
</TouchableOpacity>
</View>
);
};
Finally Slider.js
const Slider = (props) => {
const [active, setActive] = useState(false)
let transformX = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (active) {
Animated.timing(transformX, {
toValue: 1,
duration: 300,
useNativeDriver: true
}).start()
} else {
Animated.timing(transformX, {
toValue: 0,
duration: 300,
useNativeDriver: true
}).start()
}
}, [active]);
const rotationX = transformX.interpolate({
inputRange: [0, 1],
outputRange: [2, Dimensions.get('screen').width / 4]
})
return (
code for animation
)
Your Slider component needs to listen to screen focus with useFocusEffect and react accordingly. I added new props active to detect if the map screen is active.
Slider.js
import * as React from 'react';
import { useState, useEffect, useRef } from 'react'
import { View, Text, StyleSheet, Animated, TouchableOpacity, SafeAreaView, Dimensions, } from 'react-native';
import {
scale,
verticalScale,
moderateScale,
ScaledSheet,
} from 'react-native-size-matters';
import { useFocusEffect } from '#react-navigation/native';
const Slider = (props) => {
const [active, setActive] = useState(false)
let transformX = useRef(new Animated.Value(0)).current;
useFocusEffect( React.useCallback(()=>{
setActive(Boolean(props.active))
console.log()
},[props.active]))
useEffect(() => {
if (active) {
Animated.timing(transformX, {
toValue: 1,
duration: 300,
useNativeDriver: true
}).start()
} else {
Animated.timing(transformX, {
toValue: 0,
duration: 300,
useNativeDriver: true
}).start()
}
}, [active]);
const rotationX = transformX.interpolate({
inputRange: [0, 1],
outputRange: [2, Dimensions.get('screen').width / 4]
})
return (
<SafeAreaView style={{
alignItems: 'center',
backgroundColor:'transparent'
}}>
<View style={{
flexDirection: 'row',
position: 'relative',
height: 45,
width: 240,
borderRadius: 10,
backgroundColor: 'white',
marginHorizontal: 5
}}>
<Animated.View
style={{
position: 'absolute',
height: 45 - 2*2,
top: 2,
bottom: 2,
borderRadius: 10,
width: Dimensions
.get('screen').width / 3 - 3.5 ,
transform: [
{
translateX: rotationX
}
],
backgroundColor: '#d1cfcf',
}}
>
</Animated.View>
<TouchableOpacity style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}} onPress={() => {setActive(false); props.renderList() }}>
<Text>
List
</Text>
</TouchableOpacity>
<TouchableOpacity style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}} onPress={() => {setActive(true); props.renderMap() }}>
<Text>
Map
</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
export default Slider
I update Slider Component in Home.js as below.
<Slider
renderMap={goToMap}
renderList={goToList}
active={route.params && route.params.componentToShow==='Screen2'}
/>
Check full working snack:
https://snack.expo.dev/#emmbyiringiro/283f93
I try to make a little opacity animation when items touch the top of the flatlist.
The problem is that just one item rendered with the animation and not 2 Items at the same row.
I want that as soon as the top of these two Items touch the top of the Flatlistview, the opacity decrease until two others Items's top below. And so on.
I put my code here if anyone has some solution for that.
Thank you !
import React, { Component, Fragment } from 'react';
import { View, StyleSheet,Dimensions, Button, Pressable, TouchableOpacity, SafeAreaView, Text, KeyboardAvoidingView, Switch,Image,FlatList, Animated } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import Data_hobbys from '../../Data_hobbys';
import { TextInput,ProgressBar , Colors } from 'react-native-paper';
const DATA = Data_hobbys;
const numColumns = 2;
const { height } = Dimensions.get("screen");
const Item = ({ title, path, opacity }) => (
<Animated.View style = {[styles.item, {opacity: opacity}]}>
<Image style = {styles.imageStyle} source = {path}/>
<Text style={styles.title}>{title}</Text>
</Animated.View>
);
const Sports_selection = () => {
const scrollY = React.useRef(new Animated.Value(0)).current
const renderItem = ({ item, index}) => {
const opacityInputRange = [
-1,
0,
150 * index,
150 * (index + 1)
]
const opacity = scrollY.interpolate({
inputRange: opacityInputRange,
outputRange: [1 ,1 ,1 ,0]
})
return (<Item title={item.title} path = {item.path} opacity = {opacity} />
);
}
return(
<View style = {styles.main_container}>
<ProgressBar progress={0.4} style = {styles.progressBar} color = '#D1A552'/>
<Text style = {styles.introduction_text}>Do you some sport or watch it ?</Text>
<Text style = {styles.instruction_text}>Yes ? Great select it !</Text>
<Animated.FlatList
onScroll = {Animated.event(
[{ nativeEvent: {contentOffset: {y: scrollY}}}],
{useNativeDriver: true}
)}
//contentContainerStyle={{flexDirection : "row", flexWrap : "wrap"}}
columnWrapperStyle={{justifyContent: 'space-evenly'}}
data = {DATA}
renderItem = {renderItem}
keyExtractor = {(item, index) => item.id}
numColumns = {numColumns}
style={{
height: "60%",
flexGrow: 0
}}
//xscrollEventThrottle={16}
style = {styles.flatlistStyle}
/>
<TouchableOpacity style = {styles.buttonNext}>
<Text style = {styles.TextButton}>Next</Text>
</TouchableOpacity>
</View>
);
}
export default Sports_selection;
const styles = StyleSheet.create({
main_container: {
flex: 1,
backgroundColor: 'white',
},
introduction_text: {
fontSize : 30,
fontWeight : 'bold',
color : '#D1A552',
textAlign : 'left',
paddingLeft: 15,
marginTop: '30%',
},
instruction_text: {
fontSize : 20,
fontWeight : 'normal',
color : 'black',
textAlign : 'left',
paddingLeft: 15,
marginTop: '2%',
},
item: {
justifyContent: 'center',
alignContent:'center',
height : 150,
width : 150,
marginVertical: 5,
},
title: {
fontWeight : 'normal',
paddingTop: 12,
fontSize: 15,
textAlign:'center',
},
imageStyle:{
alignSelf: 'center',
width: 100,
height: 100,
},
flatlistStyle:{
marginTop:'6%',
height: "50%",
flexGrow: 0,
},
buttonNext:{
position: 'absolute',
alignSelf: 'center',
backgroundColor: "#D1A552",
borderRadius: 30,
width: "40%",
height: 45,
justifyContent: "center",
bottom: "10%",
alignItems: "center",
},
TextButton:{
fontWeight : 'bold',
color: 'white',
fontSize: 20,
textAlign:'center',
},
progressBar: {
top: 70,
width: "70%",
alignSelf: 'center',
},
})
you need to calculate the scrollY with high of item
const HIGHT_OF_ITEM=100
const HIGHT_OF_CONTAINER=HIGHT_OF_ITEM+20
const Sports_selection = () => {
const scrollY = React.useRef(new Animated.Value(0)).current;
const renderItem = ({ item, index }) => {
const input_cal=(value)=>{
return value + HIGHT_OF_CONTAINER*(Math.floor(index/numColumns))
}
const opacityInputRange = [ input_cal(0), input_cal(50), input_cal(80),input_cal(100) ];
const opacity = scrollY.interpolate({
inputRange: opacityInputRange,
outputRange: [1,0.5,0.3,0]
, extrapolate:'clamp'
});
return <Item title={item.title} path={item.path} opacity={opacity} />;
};
snack demo
I am kind of beginner in React world. In my project, i have a different number of custom accordion objects that has flatlist of text inputs. How can i handle such a input system with single button click from parent of accordion objects. I want to have one button that collect all of the current inputs and then end it on a server or any other relevant pages. (I am using functional layout with state hooks.)
Thank you for any response,
Bests
You can see the layout here !
You may see accordion.js below:
import React, { Component, useState, useEffect} from 'react';
import { View, TouchableOpacity, FlatList, StyleSheet, TextInput } from "react-native";
//import { Colors } from './Colors';
import { theme } from "../constants";
import Text from "./Text";
import Icon from "react-native-vector-icons/MaterialIcons";
import { ScrollView } from 'react-native-gesture-handler';
const Colors = {
PRIMARY: '#1abc9c',
WHITE: '#ffffff',
LIGHTGREEN: '#BABABA',
GREEN: '#0da935',
GRAY: '#f7f7f7',
LIGHTGRAY: '#C7C7C7',
DARKGRAY: '#5E5E5E',
CGRAY: '#ececec',
OFFLINE_GRAY: '#535353'
}
export default function Accordion (props) {
const [data, setData] = useState(props.data)
const [expanded, setExpanded] = useState(false)
const onClick = (index) => {
const temp = data.slice()
temp[index].value = !temp[index].value
console.log(temp)
setData(temp)
}
const toggleExpand = (section) => {
//this.setState({ expanded: !this.state.expanded )
setExpanded(prev_state => !prev_state)
props.fromparentonClick(expanded)
}
useEffect(() => {
console.log('will unmount')
}, [expanded]);
return (
<View>
<TouchableOpacity
style={styles.row}
onPress={() => toggleExpand()}
>
<Text style={[styles.title, styles.font]}>
{props.title}
</Text>
<Icon
name={
expanded
? "keyboard-arrow-up" //this is condinational ternary operator rendering :)
: "keyboard-arrow-down"
}
size={30}
color={Colors.DARKGRAY}
/>
</TouchableOpacity>
<View style={styles.parentHr} />
{ expanded && ( //this is short circuit operator
<View style={{}}>
<FlatList
data={data}
numColumns={1}
scrollEnabled={true}
renderItem={({ item, index }) => (
<View styles={styles.deneme}>
<Text style={[styles.font, styles.itemInActive]}>
{item.key}
</Text>
<TouchableOpacity
style={[
styles.childRow,
styles.button,
item.value ? styles.btnInActive : styles.btnActive
]}
onPress={() => onClick(index)}
>
<Icon
name={"check-circle"}
size={24}
color={item.value ? Colors.LIGHTGRAY : Colors.GREEN}
/>
{/* <Text style={[styles.font, styles.itemInActive]}>
{item.key}
</Text>*/}
<TextInput
style={
styles.text_input
}
blurOnSubmit
placeholder="input1"
placeholderTextColor="#60605e"
numeric
keyboardType={"numeric"}
maxLength={3}
/>
<TextInput
style={
styles.text_input
}
blurOnSubmit
placeholder="input2"
placeholderTextColor="#60605e"
numeric
keyboardType={"numeric"}
maxLength={3}
/>
<TextInput
style={
styles.text_input
}
blurOnSubmit
placeholder="input3"
placeholderTextColor="#60605e"
numeric
keyboardType={"numeric"}
maxLength={3}
/>
</TouchableOpacity>
<View style={styles.childHr} />
</View>
)}
/>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center'
},
font: {
// fontFamily: Fonts.bold,
},
button: {
width: '100%',
height: 54,
alignItems: 'center',
paddingLeft: 35,
paddingRight: 35,
fontSize: 12,
},
title: {
fontSize: 14,
fontWeight: 'bold',
color: Colors.DARKGRAY,
},
itemActive: {
fontSize: 12,
color: Colors.GREEN,
},
itemInActive: {
fontSize: 12,
color: Colors.DARKGRAY,
},
btnActive: {
borderColor: Colors.GREEN,
},
btnInActive: {
borderColor: Colors.DARKGRAY,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
height: 56,
paddingLeft: 25,
paddingRight: 18,
alignItems: 'center',
backgroundColor: Colors.CGRAY,
},
childRow: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: Colors.GRAY,
},
parentHr: {
height: 1,
color: Colors.WHITE,
width: '100%'
},
childHr: {
height: 1,
backgroundColor: Colors.LIGHTGRAY,
width: '100%',
},
colorActive: {
borderColor: Colors.GREEN,
},
colorInActive: {
borderColor: Colors.DARKGRAY,
},
text_input: {
width: 80,
backgroundColor: "#dde8c9",
padding: 10,
textAlign: 'center'
},
deneme: {
flexDirection: 'column',
textAlign: 'center',
justifyContent: 'center',
}
});
You may see parent component below:
import * as WebBrowser from 'expo-web-browser';
import React, { Component,useState } from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { Button, Block, Input,Accordion ,Header} from "../components";
import { theme } from "../constants";
//import {CATEGORIES} from "../Data/dersler";
import SwitchSelector from "react-native-switch-selector";
import { MonoText } from '../components/StyledText';
import 'core-js/es6/symbol'; import 'core-js/fn/symbol/iterator';
export default function HomeScreen (props) {
const options = [
{ label: "x", value: "1" },
{ label: "y", value: "2" }
]
const initial_state = {
courses: [
{
key: "c1",
title: "ss",
data: [
{ key: "dd", value: "false" },
{ key: "ff", value: "false" },
{ key: "gg ", value: "false" }
]
},
{
key: "c2",
title: "ss2",
data: [
{ key: "dd", value: "false" },
{ key: "ff", value: "false" },
{ key: "gg", value: "false" },
{ key: "cc", value: "false" }
]
},
],
}
const second_state = {
courses: [
{
key: "c1",
title: "dd",
data: [
{ key: "cc", value: "false" },
{ key: "dd", value: "false" },
{ key: "ff ", value: "false" }
]
},
]
}
const [exam, setExam] = useState(initial_state)
const [onlineAcc, setonlineAcc] = useState(false)
const [activeSession, setactiveSession] = useState(0)
const controlAccordions = (arg) => {
setonlineAcc(prev_state => !prev_state)
//console.log(onlineAcc)
console.log(arg)
if(arg){
setactiveSession(prev_state => prev_state -1 )
}
else {
setactiveSession(prev_state => prev_state + 1)
}
console.log(activeSession)
}
const renderAccordians = () => {
let items = [];
//console.log(`Call onPress with value: ${ this.state}`);
//console.log(exam.courses);
return exam.courses.map(ex => (<Accordion active={activeSession} fromparentonClick={controlAccordions} title={ex.title} data={ex.data} key={ex.key} /> ))
//return items;
}
return (
<View style={styles.container}>
<Header title="Anasayfa" />
<SwitchSelector
options={options}
initial={1}
buttonColor={theme.colors.gray2}
onPress={value => {
if( value== 1)
{setExam (second_state)}
else {
setExam(initial_state)
}
console.log(value)
}}
/>
<ScrollView style={styles.container}>
{renderAccordians()}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
developmentModeText: {
marginBottom: 20,
color: 'rgba(0,0,0,0.4)',
fontSize: 14,
lineHeight: 19,
textAlign: 'center',
},
contentContainer: {
paddingTop: 30,
},
welcomeContainer: {
alignItems: 'center',
marginTop: 10,
marginBottom: 20,
},
welcomeImage: {
width: 100,
height: 80,
resizeMode: 'contain',
marginTop: 3,
marginLeft: -10,
},
getStartedContainer: {
alignItems: 'center',
marginHorizontal: 50,
},
homeScreenFilename: {
marginVertical: 7,
},
codeHighlightText: {
color: 'rgba(96,100,109, 0.8)',
},
codeHighlightContainer: {
backgroundColor: 'rgba(0,0,0,0.05)',
borderRadius: 3,
paddingHorizontal: 4,
},
getStartedText: {
fontSize: 17,
color: 'rgba(96,100,109, 1)',
lineHeight: 24,
textAlign: 'center',
},
tabBarInfoContainer: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
...Platform.select({
ios: {
shadowColor: 'black',
shadowOffset: { width: 0, height: -3 },
shadowOpacity: 0.1,
shadowRadius: 3,
},
android: {
elevation: 20,
},
}),
alignItems: 'center',
backgroundColor: '#fbfbfb',
paddingVertical: 20,
},
tabBarInfoText: {
fontSize: 17,
color: 'rgba(96,100,109, 1)',
textAlign: 'center',
},
navigationFilename: {
marginTop: 5,
},
helpContainer: {
marginTop: 15,
alignItems: 'center',
},
helpLink: {
paddingVertical: 15,
},
helpLinkText: {
fontSize: 14,
color: '#2e78b7',
},
});
you will have to keep track of the data of the accordians in the parent class
let accordionData = [];
const addAccordionData = data => {
accordionData.push(data);
}
const renderAccordians = () => {
let items = [];
//console.log(`Call onPress with value: ${ this.state}`);
//console.log(exam.courses);
return exam
.courses.map(ex => (
<Accordion
active={activeSession}
fromparentonClick= {controlAccordions}
title={ex.title}
data={ex.data}
onAccordianDataSet={addAccordianData} // add this line <====
key={ex.key} /> ))
//return items;
}
in the accordion component do this
// in the **accordion ** component do this
const onClick = (index) => {
const temp = data.slice()
temp[index].value = !temp[index].value
console.log(temp)
setData(temp);
// add this line
props.onAccordianDataSet(temp); // now the answer will be in the parents
}
and you can have a button in the parent that calls a function like this
const submitDataToDatabase = () => {
if(accordionData.length === 0) {
alert("answer every question");
return;
}
submit data to database storage
}