Page slider not working On Android, works fine in iOS - javascript

The code below shows how all the functions are implemented
On iOS everything works fine, the button changes slide, the dots on the bottom change state based on the page and on the last page it displays a signup button,
On Android, the button works only on the first page, the last page doesnt show the signup button, and next button doesnt work on the second page!
export default function introScreen({ navigation }) {
const [sliderState, setSliderState] = useState({ currentPage: 0 });
const scrollRef = useRef();
const { width, height } = Dimensions.get('window')
const notchSize = StatusBar.currentHeight
const setSliderPage = (event: any) => {
const { currentPage } = sliderState;
const { x } = event.nativeEvent.contentOffset;
const indexOfNextScreen = Math.floor(x / width);
if (indexOfNextScreen !== currentPage) {
setSliderState({
...sliderState,
currentPage: indexOfNextScreen,
});
}
};
const { currentPage: pageIndex } = sliderState;
const onPressTouch = () => {
scrollRef.current?.scrollTo({
x: width*(pageIndex+1),
animated: true,
});
}
return (
<>
<StatusBar hidden />
<SafeAreaView style={{ flex: 1, backgroundColor:'black' }}>
<ScrollView
style={{ flex: 1 }}
horizontal={true}
scrollEventThrottle={16}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onScroll={(event: any) => {
setSliderPage(event);
}}
ref={scrollRef}
>
<View style={{ width, height, alignItems: 'center', }}>
<Image source={require('../../assets/images/Intorduction/1.png')} style={styles.imageStyle} />
<BlurView tint={'dark'} intensity={70} style={{width:'80%',borderRadius:10, marginTop:10}}>
<Text style={[styles.textStyle,{color:'rgb(255, 190, 46)'}]}bla bla</Text>
</BlurView>
</View>
<View style={{ width, height, alignItems: 'center' }}>
<Image source={require('../../assets/images/Intorduction/2.png')} style={styles.imageStyle} />
<BlurView tint={'dark'} intensity={70} style={{width:'80%',borderRadius:10, marginTop:10}}>
<Text style={[styles.textStyle,{color:'rgb(255, 190, 46)'}]}>bla bla</Text>
</BlurView>
</View>
<View style={{ width, height, alignItems: 'center' }}>
<Image source={require('../../assets/images/Intorduction/3.png')} style={styles.imageStyle} />
<BlurView tint={'light'} intensity={70} style={{width:'80%',borderRadius:10, marginTop:10}}>
<Text style={[styles.textStyle,{color:'rgb(241, 250, 238)'}]}>bla bla</Text>
</BlurView>
</View>
</ScrollView>
{pageIndex != 2 ?
<View style={styles.paginationWrapper}>
{Array.from(Array(3).keys()).map((key, index) => (
<View style={[styles.paginationDots, { opacity: pageIndex === index ? 1 : 0.2 }]} key={index} />
))}
</View>
:
<></>
}
{pageIndex != 2 ?
<BlurView tint={'dark'} intensity={70} style={styles.nextButton}>
<TouchableOpacity style={{flex:1, justifyContent:'center', alignItems:'center'}} onPress={onPressTouch} >
<Text style={{color:'rgb(241, 250, 238)',fontFamily: 'poiret-one', fontSize:25}}>
Next
</Text>
</TouchableOpacity>
</BlurView>
:
<BlurView tint={'light'} intensity={70} style={styles.signUpButton}>
<TouchableOpacity style={{flex:1, justifyContent:'center', alignItems:'center'}} onPress={() =>navigation.navigate('Login')} >
<Text style={{color:'black',fontFamily: 'poiret-one', fontSize:25}}>
Sign Up
</Text>
</TouchableOpacity>
</BlurView>
}
</SafeAreaView>
</>
);
}

I have narrowed down the problem to the width and the 'event.nativeEvent.contentOffset', the returning number is close to 1 but it is (0.9), therefor math.floor was giving 0 as the index number,
I have changed Math.floor to Math.round and it is working perfectly fine,
If anyone wants a Introduction Slider for their app they can use this

Related

Flatlist scroll and scrollview scroll is not working inside modal

I am making filter modal for e-commerce application where I divided it into two component view one for left and one for right view. Where I am using flatlist for left view and scrollview for right.
I am trying achieve scroll on both side. I don't know why isn't working.Same code is working for different project without any problem.
I am unable to scroll on either side.
Attaching code snippet for same.
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {
this.setState({ modalVisible: !this.state.modalVisible });
}}
>
<View style={Style.general.centeredView}>
<View style={Style.general.modalView}>
<View style={Style.general.filterModal}>
<Text style={Style.general.filterModalHeading}>FILTERS</Text>
{this.state.filtersApplied.length > 0 ? (
<TouchableOpacity
style={{
alignSelf: "flex-end",
marginRight: 10,
paddingHorizontal: 10,
paddingVertical: 5,
borderWidth: 1,
borderColor: "#000",
borderRadius: 5,
}}
onPress={() => this.clearFiltersHandler()}
>
<Text>Clear Filters</Text>
</TouchableOpacity>
) : null}
</View>
<View style={Style.general.filterModalData}>
<View style={Style.general.filterModalDataLeftScroll}>
{/* {isFetching && modalVisible ? <Loader /> : null} */}
{this.state.filters && (
<FlatList
// contentContainerStyle={styles.listContainer}
data={this.state.filters}
// numColumns={2}
// onEndReached={() => fetchProducts(false, sortApplied)}
// onEndReachedThreshold={0.1}
keyExtractor={() =>
"_" + Math.random().toString(36).substr(2, 9)
}
renderItem={this.renderFilters}
// removeClippedSubviews
/>
)}
</View>
<ScrollView
style={{
flexGrow: 1,
width: "70%",
flexDirection: "column",
paddingHorizontal: 10,
}}
>
{this.state.activeFilterLabel !== "pf_p_price" &&
this.state.fliterLabel.map((values, i) => {
// console.log("values", values);
return (
<TouchableOpacity
key={i}
onPress={() => this.addFilterHandler(values)}
>
<View
style={Style.general.filterModalDataRightFilter}
>
<View
style={[
Style.general.filterBoxWrap,
{
backgroundColor:
this.state.filtersApplied.some(
(filter) => filter == values.key
)
? "#000"
: "#fff",
},
]}
>
<Icon name="check" size={11} color="#fff" />
</View>
<Text
style={{
fontFamily: "Futura",
fontSize: 12,
}}
>
{values.key}
</Text>
</View>
</TouchableOpacity>
);
})}
{this.state.activeFilterLabel === "pf_p_price" ? (
<View>
<Text>Hello</Text>
</View>
) : null}
</ScrollView>
</View>
{this.state.filterModalLoader ? (
<TouchableOpacity style={{ flex: 1 }} />
) : null}
<View style={Style.general.filterBtnFix}>
<TouchableWithoutFeedback
onPress={() => {
this.setState({ modalVisible: false });
}}
>
<View style={Style.general.filterBtnFixWrap}>
<Text style={Style.general.filterBtnFixWrapText}>
Close
</Text>
</View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => {
this.setState({ modalVisible: false });
}}
>
<View style={Style.general.filterBtnFixWrap}>
<Text style={Style.general.filterBtnFixWrapText}>
Apply
</Text>
</View>
</TouchableWithoutFeedback>
</View>
</View>
</View>
</Modal>

React Native keyboard disappears after each letter press

I am working on a register page on react native where I want to make an onboarding register page so I have my TextInputs in a separate function to switch between the text input when you go to the next onboard screen. But when I press on an input and want to start typing I can get one press of on the keyboard before it goes down again.
I have tried moving my useEffect inside that function then the issue is solved but the other functions cannot get the values then.
//Content
export default function Register({ navigation }) {
const [name, onNameChange] = useState("");
//get current slide index point
const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
const ref = React.useRef(null);
//update slides upon index
const updateCurrentSlideIndex = (e) => {
const contentOffsetX = e.nativeEvent.contentOffset.x;
const currentIndex = Math.round(contentOffsetX / width);
setCurrentSlideIndex(currentIndex);
};
//button function to go to next slide
const goNextSlide = () => {
const nextSlideIndex = currentSlideIndex + 1;
if (nextSlideIndex != slides.length) {
const offset = nextSlideIndex * width;
ref?.current?.scrollToOffset({ offset });
setCurrentSlideIndex(nextSlideIndex);
}
};
const RegInputs = () => {
if (currentSlideIndex == 0) {
return (
<KeyboardAvoidingView style={{ flex: 1 }} behavior="position">
<BlurView intensity={20} tint="light">
<Text style={styles.label}>Name</Text>
<TextInput
value={name}
onEndEditing={(text) => onNameChange(text)}
style={styles.input}
errorStyle={{ color: "red" }}
errorMessage="Incorrect Username"
/>
</BlurView>
</KeyboardAvoidingView>
);
} else if (currentSlideIndex == 1) {
return <View></View>;
} else if (currentSlideIndex == 2) {
return <View></View>;
}
};
const handleRegisterPress = () => {
Alert.alert("NAME: " + name);
};
const Slide = ({ item }) => {
return (
<View style={{ alignItems: "center" }}>
<View style={{ width }}>
<Image
source={item.image}
style={{
width: imagewidth,
height: imageHeight,
resizeMode: "contain",
alignSelf: "center",
}}
/>
</View>
<Text style={styles.heading}>{item.title}</Text>
<RegInputs />
</View>
);
};
const Footer = () => {
return (
<View style={{ height: height * 0.15, width, paddingHorizontal: 20 }}>
<View
style={{
flexDirection: "row",
justifyContent: "center",
marginTop: 20,
}}
>
{slides.map((_, index) => (
<View
key={index}
style={[
styles.indicator,
currentSlideIndex == index && {
backgroundColor: "white",
borderColor: "#68BF7B",
borderWidth: 2,
width: 16,
opacity: 1,
},
]}
/>
))}
</View>
<View style={{ marginBottom: 20 }}>
{currentSlideIndex == slides.length - 1 ? (
<View>
<TouchableOpacity onPress={handleRegisterPress}>
<View style={styles.nextbtn}>
<Text style={styles.nextbtnText}>Finish</Text>
</View>
</TouchableOpacity>
</View>
) : (
<TouchableOpacity onPress={goNextSlide}>
<View style={styles.nextbtn}>
<Text style={styles.nextbtnText}>Next</Text>
</View>
</TouchableOpacity>
)}
<View style={{ flexDirection: "row" }}>
<Text style={styles.bodyreg}>Already have an account?</Text>
<TouchableOpacity onPress={() => navigation.navigate("Login")}>
<Text style={styles.bodylinkreg}>Login</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
};
}
change the prop onEndEditing in TextInput to onChangeText, and reload the entire app

Automatically scroll ScrollView in KeyboardAvoidingView when I add a new TextInput

I am working with a KeyboardAvoidingView and it's working perfectly fine except for one small issue. In the code below I have a TouchableOpacity that when clicked runs the function addName() which appends to an array and creates a new TextInput - basically when you click it, it adds a new TextInput to the ScrollView.
The KeyboardAvoidingView works perfectly fine except every time a new TextInput is added/rendered, I have to scroll down to see it. Do you know how I can make it automatically scroll to the bottom when a new TextInput is rendered?
Here is my code for the KeyboardAvoidingView:
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS == "ios" ? "padding" : "height"}
>
<HeaderComponent
name={this.props.route.params.bill.barName + " Tab"}
navigation={this.props.navigation}
goHome={true}
goHomePrompt={true}
/>
<View
style={{
marginTop: 30,
marginLeft: 10,
}}
>
<Text
style={{ color: "white", fontSize: 18 }}
allowFontScaling={false}
>
Add people to split with...
</Text>
</View>
<ScrollView keyboardShouldPersistTaps={"handled"}>
{this.state.nameInput.map((value, index) => (
<View style={styles.nameContainer} key={index}>
<View
style={{
width: "90%",
}}
>
<TextInput
style={styles.textInputContainer}
value={value}
onChange={this.handleText(index)}
placeholder={"Enter name..."}
placeholderTextColor={"#333333"}
maxLength={50}
/>
</View>
<View
style={{
width: "10%",
}}
>
<TouchableOpacity
onPress={() => this.handleDelete(index)}
>
<Icon name="cancel" type="material" color="red" />
</TouchableOpacity>
</View>
</View>
))}
<TouchableOpacity onPress={() => this.addName()}>
<Text style={styles.addPerson}>+ Add Person</Text>
</TouchableOpacity>
</ScrollView>
<View style={styles.bottomContainer}>
<TouchableOpacity
style={styles.continueButton}
onPress={() => {
// filters through array to make sure there are no empty strings
let nameInput = this.state.nameInput.filter(
(name) => name !== ""
);
if (
nameInput.length > 1 &&
new Set(nameInput).size === nameInput.length
) {
this.setState({ nameInput, loading: false });
} else {
alert(
"Please make sure there are at least two people and no duplicate names!"
);
}
}}
>
<Text style={styles.continueButtonText} allowFontScaling={false}>
Continue to Split Tab
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
And here is my code for the addName() function:
addName = () => {
let nameInput = this.state.nameInput.concat("");
this.setState({
nameInput,
});
};
This page has the solution I was looking for: Is it possible to keep a ScrollView scrolled to the bottom?

Different content on React Native based on condition

on my react-native app I would like to have premium content for people who paid a subscription.
My issue is how I make the content to display as unavailable(if you are not premium ) and the same as the other content if you are premium. Basically I would like the premium content to be displayed with a "lock overlay" on it for non-premium users.
However, I do not know how I set this conditional. It is a matter of state? If yes where should be positioned this state considering that is unidirectional?
Just to be clear I will have premium and non premium content
class Browser extends Component {
scrollX = new Animated.Value(0);
renderRecommended = () => {
return (
<View style={[styles.flex, styles.column, styles.recommended]}>
<View style={[styles.row, styles.recommendedHeader]}>
<Text
style={{
fontSize: theme.sizes.font * 1.4,
alignSelf: 'center',
color: 'white',
fontFamily: 'Nunito-Bold',
}}>
Recommended
</Text>
</View>
<View style={[styles.column, styles.recommendedList]}>
<FlatList
horizontal
scrollEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
snapToAlignment="center"
style={[styles.shadow, {overflow: 'visible'}]}
data={this.props.destinations}
keyExtractor={(item, index) => `${item.id}`}
renderItem={({item, index}) =>
this.renderRecommendation(item, index)
}
/>
</View>
</View>
);
};
renderRecommendation = (item, index) => {
const {destinations} = this.props;
const isLastItem = index === destinations.length - 1;
const {navigation} = this.props;
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('PreScreen', {
item,
})
}>
<View
style={[
styles.flex,
styles.column,
styles.recommendation,
styles.shadow,
index === 0 ? {marginLeft: theme.sizes.margin} : null,
isLastItem ? {marginRight: theme.sizes.margin / 2} : null,
]}>
<ImageBackground
style={[styles.imageback]}
source={{uri: item.preview}}
/>
</View>
<View
style={[
index === 0 ? {marginLeft: theme.sizes.margin - 10} : null,
isLastItem ? {marginRight: theme.sizes.margin / 2} : null,
]}>
<Text
style={{
fontSize: theme.sizes.font * 1.25,
fontWeight: '200',
color: 'white',
marginLeft: 10,
//paddingBottom: 20,
fontFamily: 'Nunito-Bold',
}}>
{item.title}
</Text>
<Text
style={{
color: theme.colors.caption,
marginLeft: 10,
fontFamily: 'Nunito-SemiBold',
}}>
{item.location}
</Text>
</View>
</TouchableOpacity>
);
};
render() {
return (
<ScrollView style={styles.container}>
<BackgroundSvg style={styles.background} />
<ScrollView
style={styles.contentContainer}
showsVerticalScrollIndicator={false}
contentContainerStyle={{paddingBottom: theme.sizes.paddin}}>
{this.renderRecommended()}
{this.renderRecommended2()}
{/* <View style={styles.mainContainerView}>
<TouchableOpacity style={styles.singInButton} gradient>
<Text style={styles.logInText}>
Activate premium subscription
</Text>
</TouchableOpacity>
</View> */}
</ScrollView>
</ScrollView>
);
}
}
Browser.defaultProps = {
destinations: mocks,
reading: readingList,
};
export default Browser;
My code is the one on the top. Just to simplify I am accesing some elements from JSON and I am creating Flatlist based on this. What I want is is to give some of the JSON files a bolean with premium or not and in this way to make some elements available for user or not.
As part of your destinations array, for each object, you can specify a boolean field called isPremiumItem. Your users should have a boolean field like isPremiumUser to show the type of their subscription.
Then in renderRecommended method you can check the user subscription status (isPremiumUser), and also the specific item status (isPremiumItem). Then you can render accordingly.

Animate the dynamic height of a view on scroll

I have a UI similar to the image below:
The Title section (the section in dark blue) is supposed to be fixed and the Body section is scrollable.
The height of the Title section would be dynamic as there can be 4 - 5 bullet points retrieved from the server.
My task is to start reducing the height of the Title section when the user starts scrolling upwards to read the content that's present beneath until the point where the user can only read the title in the header. Also, when the user starts scrolling downwards to the point where he/she can see the heading Body, I am supposed to start increasing the height so that he can again start seeing all the bullet points.
This feature is more of like hiding/showing the header on the scroll. But, the title will always be visible.
I have written the below-written code to achieve the same:
import React from 'react';
import {
View,
Text,
ScrollView,
TouchableOpacity,
Linking,
Image,
Animated,
} from 'react-native';
class App extends React.PureComponent<Props, States> {
constructor() {
super();
this.state = {
headerHeight: 0,
headerTitleHeight: 0,
};
this.scrollY = new Animated.Value(0);
}
render() {
const { navigation, pending, macroFeed } = this.props;
const { headerHeight, headerTitleHeight } = this.state;
const { themeDetails } = navigation.state.params;
const {
title, bullet1, bullet2, bullet3,
} = themeDetails;
let headerStyle = {};
if (headerHeight) {
headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, headerHeight - headerTitleHeight],
outputRange: [headerHeight, headerTitleHeight],
extrapolate: 'clamp',
}),
};
}
const sortedMacroFeed = _.sortBy(macroFeed, (o) => moment(o.date).format('YYYYMMDD')).reverse().slice(0, 5);
if (pending) {
return (
<View style={styles.maxFlex}>
<LoadingSpinner
size="large"
containerStyle={styles.loadingSpinner}
/>
</View>
);
}
return (
<View
style={styles.maxFlex}
>
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}}
>
<View style={styles.macroBgWrapper}>
<Image source={themeDetails.imgUrl} style={styles.macroBg} />
<View style={styles.macroBgOverlay} />
</View>
<View
style={styles.header}
onLayout={(event) => {
this.setState({
headerTitleHeight: event.nativeEvent.layout.height,
});
}}
>
<TouchableOpacity onPress={() => navigation.goBack()}>
<View>
<Icon name="ios-arrow-back" size={32} style={styles.backIcon} />
</View>
</TouchableOpacity>
<View style={styles.titleWrap}>
<Text style={styles.headerTitle}>
{title}
</Text>
</View>
</View>
<View style={styles.bulletWrapper}>
{
!!bullet1 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet1}
</Text>
</View>
</View>
</View>
)
}
{
!!bullet2 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet2}
</Text>
</View>
</View>
</View>
)
}
{
!!bullet3 && (
<View style={styles.column}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={styles.buttetListText}>
{'\u2022'}
{' '}
</Text>
</View>
<View style={styles.bulletText}>
<Text style={styles.buttetListText}>
{bullet3}
</Text>
</View>
</View>
</View>
)
}
{
!bullet1 && !bullet2 && !bullet3 && (
<View style={styles.noBulletWrapper}>
<Text style={styles.noBulletPoints}>
No description found.
</Text>
</View>
)
}
</View>
</Animated.View>
<ScrollView
style={styles.maxFlex}
showsVerticalScrollIndicator={false}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: this.scrollY } } },
])}
scrollEventThrottle={16}
>
<View style={[styles.section, styles.wrapGutter]}>
<Text style={styles.sectionTitle}>
Recent Headlines
</Text>
{
sortedMacroFeed.map((feed) => (
<View key={feed.id} style={styles.newsSection}>
<Text style={styles.newsHours}>
{moment(feed.date).fromNow()} | {feed.author}
</Text>
<Text style={styles.newsTitle}>
{feed.title}
<Text onPress={() => this.openWebView(feed.url)}>
<EvilIcons name="external-link" size={16} style={styles.externalLinkIcon} />
</Text>
</Text>
</View>
))
}
</View>
<View style={styles.section}>
<View style={[styles.wrapGutter, styles.sectionTitleWrap]}>
<Text style={styles.sectionTitle}>
Exposure
</Text>
<View style={styles.totalNavWrap}>
<Text style={styles.totalNav}>
$467M
</Text>
<Text style={styles.totalNavLabel}>
Total NaV (all portfolios)
</Text>
</View>
</View>
<Tabs
tabsData={TABS_DATA}
renderTabContent={this.renderTabContent}
tabName="title"
hideIfOneTab
/>
</View>
</ScrollView>
</View>
);
}
}
If you have observed I am trying to retrieve the dynamic height of the title section using onLayout.
This code only works one way i.e when I scroll up. The height of the title section reduces to the point where only the title can be seen and the bullet points get hidden. But after that, I cannot scroll down. The height gets permanently reduced.
Now, if I change the below-given code:
let headerStyle = {};
if (headerHeight) {
headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, headerHeight - headerTitleHeight],
outputRange: [headerHeight, headerTitleHeight],
extrapolate: 'clamp',
}),
};
}
to
const headerStyle = {
height: this.scrollY.interpolate({
inputRange: [0, 240],
outputRange: [300, 60],
extrapolate: 'clamp',
}),
};
everything seems to work fine. Basically, if I stop retrieving the value of the height dynamically and provide a static value like 240 or something, everything seems to work fine.
But, animation on scroll stops if I accept dynamic height. Any help to solve this would be much appreciated. Thanks in anticipation.
I found the issue. So, if you observe the line:
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}}
>
onLayout used to setState as soon as the headerHeight value changed on the scroll. Due to which the height of the header used to reduce and scrolling the content down was not possible.
I changed the code to:
<Animated.View
style={[styles.headerWrapper, headerStyle]}
onLayout={(event) => {
if (!this.state.headerHeight) {
this.setState({
headerHeight: event.nativeEvent.layout.height,
});
}
}}
>
Now, everything works fine.

Categories

Resources