react native: change style property on scroll scrollview - react-native

I have a view (flex: 0) above a scrollview (flex: 1). When I scroll down on the scrollview (event.nativeEvent.contentOffset.y > 0), I want to change the border bottom color of the top view.
Current code:
const Screen = (props) => {
// ...
const [bottom, setBottom] = useState(0);
const scrollE = (event) => {
setBottom(event.nativeEvent.contentOffset.y > 0 ? 1 : 0);
};
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 0, borderBottomWidth: bottom, borderBottomColor: 'black' }}>
<Text>HEADER</Text>
</View>
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ flexGrow: 1 }} onScroll={scrollE} scrollEventThrottle={16}>
// ... scrollview content
</ScrollView>
</View>
);
};
It seems like my screen is flashing when scrolling. I think it has something to do with the re-renders everytime my border state changes. I tried changing it with use ref to this, but this does not work either:
const Screen = (props) => {
// ...
const bottom = useRef(0);
const scrollE = (event) => {
bottom.current = event.nativeEvent.contentOffset.y > 0 ? 1 : 0;
};
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 0, borderBottomWidth: bottom.current, borderBottomColor: 'black' }}>
<Text>HEADER</Text>
</View>
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ flexGrow: 1 }} onScroll={scrollE} scrollEventThrottle={16}>
// ... scrollview content
</ScrollView>
</View>
);
};
I do not need a "sticky" header that resizes, I only want the border bottom width to change.

I would do it with animated API, hope this helps
const Screen = (props) => {
const scrollY= new Animated.Value(0)
const scrollE = (event) => {
Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }],{ useNativeDriver: true })
};
const bottom = scrollY.interpolate({
inputRange: [0, 10],
outputRange: [1, 0],
})
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 0, borderBottomWidth: bottom, borderBottomColor: 'black' }}>
<Text>HEADER</Text>
</View>
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ flexGrow: 1 }} onScroll={scrollE} scrollEventThrottle={16}>
// ... scrollview content
</ScrollView>
</View>
);
};

Related

hotizonatal scroll in ScrollView on click of button react native

I am using horizontal ScrollView to show few items on screen, this scroll view has pagination enabled and I am able to show pagination on the screen as well.
Below is my code:
const SwipeScreen = () => {
const { width, height } = Dimensions.get('window');
const [sliderState, setSliderState] = useState({ currentPage: 0 });
const setSliderPage = (event) => {
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 scrollViewRef = useRef(null);
const toNextPage = () => {
sliderState.currentPage += 1;
scrollViewRef.current?.scrollTo({
x: window.width * sliderState.currentPage,
animated: true,
});
};
return (
<>
<ScrollView
style={{ flex: 1 }}
ref={scrollViewRef}
horizontal={true}
scrollEventThrottle={16}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
onScroll={(event) => {
setSliderPage(event);
}}
>
<View style={{ width, height }}>
<Text>Item 1</Text>
<Button title='Next' color='#f194ff' onPress={toNextPage} />
</View>
<View style={{ width, height }}>
<Text>Item 2</Text>
<Button title='Next' color='#f194ff' onPress={toNextPage} />
</View>
<View style={{ width, height }}>
<Text>Item 3</Text>
<Button title='Next' color='#f194ff' onPress={toNextPage} />
</View>
<View style={{ width, height }}>
<Text>Item 4</Text>
<Button title='Next' color='#f194ff' onPress={toNextPage} />
</View>
<View style={{ width, height }}>
<Text>Item 5</Text>
</View>
</ScrollView>
<View style={styles.paginationWrapper}>
{Array.from(Array(5).keys()).map((key, index) => (
<View
style={[
styles.paginationDots,
{ opacity: pageIndex === index ? 1 : 0.2 },
]}
key={index}
/>
))}
</View>
</>
);
};
const styles = StyleSheet.create({
textColor: {
color: 'red',
},
paginationWrapper: {
position: 'absolute',
bottom: 200,
left: 0,
right: 0,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
},
paginationDots: {
height: 10,
width: 10,
borderRadius: 10 / 2,
backgroundColor: '#0898A0',
marginLeft: 10,
},
});
export default WelcomeSwipeImgScreen;
I also wanted a next button, on whose click next item is shown on screen. For this I have written toNextPage function, but its not working. I am not able to figure out where I am going wrong with this

React-Native-Swiper images Overflow ONLY ON ANDROID

I have a problem with react-native-swiper on Android ONLY. The same code works for iOS!
Below is an image of what is happening:
The area is blue is supposed to be a square and all images should be within it. Something like this (I only render 1 image here):
This is what my code looks like:
renderSwiper = () => {
const images = this.props.data.item[1].images;
let swiperImages = images.map((image, index) => {
let priority = FastImage.priority.normal;
if (index === 0)
priority = FastImage.priority.high;
return (
<TouchableWithoutFeedback key={index} onPress={this.routeToListingDetails} >
<FastImage
style={styles.imageStyle}
source={{uri: image, priority: priority}}
/>
</TouchableWithoutFeedback>
)
})
return (
<View style={{ aspectRatio: 1, width: '100%'}}>
<Swiper
loop={false}
paginationStyle={styles.swiperPaginationStyle}
>
{swiperImages}
</Swiper>
</View>
);
}
render(){
return (
<View style={styles.container} >
<View style={{ borderColor: 'blue', borderWidth: 2 }}>
{this.renderSwiper()}
</View>
</View>
)
}
const styles = StyleSheet.create({
container:{
width:'50%',
alignItems:'center',
marginBottom:'4%',
padding:'2%',
},
imageStyle:{
resizeMode: "cover",
width: "100%",
aspectRatio:1
}
})

How to set the height of the image as the flex height

In my react-native application, I want to set the height of an image as the flex height. How can I do that? At present, Im using the height as the device-heigth / 3. But when it comes to the smaller screens, it makes issues. How can I achieve this as the height of the image as the flex height? My code works properly in 5 inch devices, but when it comes to 4, the image makes a mess.
render() {
return (
<View style={styles.container}>
<View style={styles.postCommentWrapper}>
<ScrollView
ref={view => {
this.scrollView = view;
}}
>
<Animated.View>
<PostProfileBar
profile={this.state.post.author}
timeAgo={this.state.post.timeAgo}
/>
<ClickableImage
source={{ uri: this.state.post.postImage }}
height={height * (3 / 10)}
width={width}
onPress={() => alert("Image Clicked")}
/>
</Animated.View>
<CommentFlatList
data={this.state.data}
refreshing={this.state.refreshing}
/>
</ScrollView>
</View>
<View
style={[
styles.commentInputWrapper,
{ flex: this.state.commentBoxStyles.flex }
]}
>
<InputWithClickableIcon
iconName="upload"
placeholder="Type Comment"
isFocused={true}
fontSize={this.state.comment.value.length > 0 ? 16 : 20}
value={this.state.comment.value}
multiline={true}
maxLength={500}
height={this.state.commentBoxStyles.height}
borderRadius={this.state.commentBoxStyles.borderRadius}
onChangeText={value => this.onChangeComment(value)}
onPress={() => this.uploadComment()}
invalidInput={styles.invalidInput}
valid={this.state.comment.valid}
touched={this.state.comment.touched}
disabled={!this.state.comment.valid}
/>
</View>
</View>
);
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle: "Comment",
headerTitleStyle: {
paddingLeft: "20%",
paddingRight: "20%"
},
headerStyle: {
paddingRight: 10,
paddingLeft: 10
},
headerLeft: (
<Icon
name={"close"}
size={30}
onChangeText={this.onChangeText}
onPress={() => {
navigation.goBack();
}}
/>
)
};
};
}
const styles = StyleSheet.create({
container: {
borderWidth: 3,
borderColor: "yellow",
flex: 1
},
postCommentWrapper: {
borderWidth: 2,
borderColor: "blue",
flex: 16,
marginTop: 10
},
// commentListWrapper: {
// borderWidth: 2,
// borderColor: "green",
// flex: 8,
// marginTop: 10
// },
commentInputWrapper: {
flex: 2,
borderWidth: 2,
borderColor: "purple",
justifyContent: "flex-end",
marginTop: 5
}
});
ClickableImage Component
import React from "react";
import { TouchableOpacity, Image, StyleSheet } from "react-native";
const clickableImage = props => (
<TouchableOpacity onPress={props.onPress}>
<Image
{...props}
style={[
styles.image,
props.style,
{ height: props.height },
{ width: props.width }
]}
/>
</TouchableOpacity>
);
const styles = StyleSheet.create({
image: {
marginTop: 10,
flexDirection: "row",
alignItems: "center"
}
});
export default clickableImage;
Since you've already passed the style props to the ClickableImage, therefore you can do
<ClickableImage
source={{ uri: this.state.post.postImage }}
style={{flex: 1}}
onPress={() => alert("Image Clicked")}
/>
You also need to pass the styles to TouchableOpacity for the flex inside the container to work
<TouchableOpacity style={props.style} onPress={props.onPress}> //... Pass the relevant styles here

How to change height of <FlatList/> in react native?

I want to change width and height of <FlatList />.
I set the height style to the current <FlatList /> but it never worked.
I can change the height of <FlatList /> in no way.
This is my render() function and styles.
render() {
const listData = [];
listData.push({ key: 0 });
listData.push({ key: 1 });
listData.push({ key: 2 });
listData.push({ key: 3 });
listData.push({ key: 4 });
return (
<View style={styles.container}>
<FlatList
data={listData}
renderItem={({item}) => {
return (
<View
style={{
width: 30, height: 30, borderRadius: 15, backgroundColor: 'green'
}}
/>
)
}}
horizontal
style={styles.flatList}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
flatList: {
height: 50,
backgroundColor: 'red'
}
});
And this is result of this code.
I found the answers for several hours but nothing helped me.
I am not sure why height style is not working.
Any help is appreciated.
adding flexGrow: 0 to the flatList style worked for me, so it will be:
flatList: {
height: 50,
backgroundColor: 'red',
flexGrow: 0
}
Set the height of <View/> and place <FlatList/> inside that <View/>
Add flexGrow: 0. Don't mention height, the design might break when it comes to responsive
Example :
<FlatList
style={{
flexGrow: 0,
}}
data={data}
renderItem={({item}) => (
<View>
<Text>{item.text}</Text>
</View>
)}
/>
FlatList has prop contentContainerStyle. You can use it to style wrapper around FlatList. FlatList inherit this prop from ScrollView read hear
you can add flexGrow: 0 to the flatList style worked for me, so it will be:
<FlatList
{...{otherProps}}
style={{
height: 50,
backgroundColor: 'red',
flexGrow: 0
}}
/>
give width then height working according to data
<View style={{maxHeight:"50%",width:"60%"}}>
<FlatList
data={this.props.data}
renderItem={({ item }) => <Text>{item.name}</Text>}
keyExtractor={(item, index) => index}
/>
</View>
<View style={styles.flatList}>
<FlatList
keyExtractor = { this.keyExtractor }
data = { this.getPOs() }
ListEmptyComponent = { this.renderEmpty }
ItemSeparatorComponent = { Separator }
renderItem = { this.renderItem }
/>
</View>
for me works add flex: 1 to the view
const styles = StyleSheet.create({
flatList: {
flex: 1,
}
})
render() {
const listData = [];
listData.push({ key: 0 });
listData.push({ key: 1 });
listData.push({ key: 2 });
listData.push({ key: 3 });
listData.push({ key: 4 });
return (
<View style={styles.container}>
<FlatList
data={listData}
renderItem={({item}) => {
return (
<View
style={{
width: 30, height: 30, borderRadius: 15, backgroundColor: 'green'
}}
/>
)
}}
horizontal
style={styles.flatList}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
height:100
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
flatList: {
backgroundColor: 'red'
}
});

React-Native: how to fill fullsize screen without explicit Width and Height?

I want to create a fullsize-screen with the child-view (a video player) that is rendered over the full size of the screen. I only get it work when i pass explicitly width and height to the component.
But i know that there is a property called "flex". In many tutorials they do something like "flex: 1", but for me it nowhere does what it is supposed to.
(For the sake of completeness, the video-player is not part of the question. I can also replace the <video> tag with <Image> or each other kind of view and get the same results)
render() {
const uri = this.props.uri
return (
<KeyboardAwareScrollView keyboardShouldPersistTaps="always" >
<TouchableWithoutFeedback onPress={RouterActions.pop}>
<Video source={{uri: uri}}
ref={(ref) => {
this.player = ref
}}
style={s.fullsize}
/>
</TouchableWithoutFeedback>
<TouchableOpacity onPress={RouterActions.pop} style={s.closeBtn}>
<Icon name="times-circle-o" size={20} color="white" />
</TouchableOpacity>
</KeyboardAwareScrollView>
)
}
My styles:
This is only working when i pass the width and height:
const s = StyleSheet.create({
fullsize: {
backgroundColor: 'black',
//flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
closeBtn: {
position: 'absolute',
left: 20,
top: 25,
},
});
I only tried out this one, but then the screen will be empty because of the -Component has a width and height of 0 each.
const s = StyleSheet.create({
fullsize: {
backgroundColor: 'black',
flex: 1, // this is not working
left: 0,
right: 0,
top: 0,
bottom: 0
},
});
I believe doing flex: 1 will make it take up all the space provided by it's parent element. In your case, none of your parent elements have any styling so try this out:
render() {
const uri = this.props.uri
return (
<View style={{ flex: 1 }}>
<TouchableWithoutFeedback style={{ flex: 1 }} onPress={RouterActions.pop}>
<Video source={{uri: uri}}
ref={(ref) => {
this.player = ref
}}
style={{ flex: 1 }}
/>
</TouchableWithoutFeedback>
<TouchableOpacity onPress={RouterActions.pop} style={s.closeBtn}>
<Icon name="times-circle-o" size={20} color="white" />
</TouchableOpacity>
</View>
)
}