hotizonatal scroll in ScrollView on click of button react native - 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

Related

White background in react-native-view-shot

I am using react-native-view-shot to save a screenshot of a view, whose height is not initially defined, as I am using padding and setting the height of the view using the onLayout method.
The problem is that, when the view has an initial fixed height, the screenshot taken does not have a white background, which is what I want. However, when I set the height when the onLayout is invoked, the screenshot has a white background.
Here's my code:
const [height, setHeight] = useState();
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
ref={contentRef}
style={{
height,
width: width - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={styles.text}>This is a test using padding</Text>
</View>
https://snack.expo.dev/#pietroputelli/react-native-view-shot
=========== EDIT ===========
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<View ref={shotRef} style={{ backgroundColor: "transparent" }}>
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: width / 2 - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: "white" }}>This is a test using padding</Text>
</View>
</View>
<Button
onPress={() => {
captureRef(shotRef, {
format: "png",
quality: 0.8,
}).then(
async (uri) => {
await MediaLibrary.saveToLibraryAsync(uri);
},
(error) => console.error("Oops, snapshot failed", error)
);
}}
title="Take screenshot"
/>
</View>
I can able to generate the same from viewshot:
take another view and give a reference to that view and generate a screenshot from that. might be its issue of reference. Please check the below screenshot.
<View ref={viewshotRef}
style={{
// whatever you want to add as per your requirement
}} >
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: DEVICE_WIDTH / 2 - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: 'white' }}>This is a test using padding</Text>
</View>
</View>
For base64:
import * as MediaLibrary from "expo-media-library";
import React, { useRef, useState } from "react";
import {
Button,
Dimensions,
StyleSheet,
Text,
View,
} from "react-native";
import ViewShot, { captureRef } from "react-native-view-shot";
const { width } = Dimensions.get("window");
export default function ViewCapture() {
const contentRef = useRef();
const [height, setHeight] = useState(undefined);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "transparent" }}>
<ViewShot ref={contentRef} options={{ result: 'base64' }}>
<View
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height);
}}
style={{
height,
width: width - 12,
backgroundColor: "darkblue",
borderRadius: 32,
}}
>
<Text style={{ color: "white" }}>This is a test using padding</Text>
</View>
</ViewShot>
{/* </View> */}
<Button
onPress={() => {
contentRef.current.capture().then((url) => {
console.log("on success", "data:image/jpeg;base64,", url)
// await MediaLibrary.saveToLibraryAsync(uri);
},
(error) => console.error("Oops, snapshot failed", error)
);
}}
title="Take screenshot"
/>
</View>
);
}
For File:
You need to set the result as tmpfile so you will get file uri in the callback
<ViewShot ref={contentRef} options={{ result: 'tmpfile' }}>
I hope it will work!

flatlist slow performance on large data react native

I'm new to react native and I'm having some performance problem with flatlist in React Native Expo. On my screen I'm displaying a list of contacts and I want them to be selected or deselected and I have managed to achieve that part but the problem is that it takes a lot of visibility time to check the contacts or to uncheck them. Making the screen not very user friendly. I have tried a lot of solutions that I've found on internet regarding this problem but none of them had worked. Hope someone can help me. Below I'll attach the code!
function ContactList() {
const [itemChecked, setItemChecked] = useState([]);
const [checked, setChecked] = useState(false);
const [contacts, setContacts] = useState([]);
const [filter, setFilter] = useState([]);
const [search, setSearch] = useState('');
const [checkedBox, setCheckedBox] = useState(false);
useEffect(() => {
(async () => {
const { status } = await Contacts.requestPermissionsAsync();
if (status === 'granted') {
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.PhoneNumbers],
// fields: [Contacts.Fields.Name],
});
if (data.length > 0) {
setContacts(data);
setFilter(data);
// console.log('contact', contacts[1]);
// console.log('filter', filter);
}
}
})();
}, []);
const searchFilter = (text) => {
if (text) {
const newData = contacts.filter((item) => {
const itemData = item.name ? item.name.toUpperCase() : ''.toUpperCase();
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
setFilter(newData);
setSearch(text);
} else {
setFilter(contacts);
setSearch(text);
}
};
const onChangeValue = (item, index) => {
if (itemChecked.includes(item.phoneNumbers[0].digits)) {
itemChecked.splice(itemChecked.indexOf(item.phoneNumbers[0].digits), 1);
} else {
itemChecked.push(item.phoneNumbers[0].digits);
setCheckedBox(true);
}
setItemChecked(itemChecked);
console.log(itemChecked);
// console.log(item);
};
const renderItem = ({ item, index }) => {
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{ flexDirection: 'row', flex: 1 }}>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey', marginBottom: 15 }}>
<Text onPress={() => setChecked(true)} style={{ fontSize: 20, marginHorizontal: 10 }}>
{item.name + ' '}
</Text>
<Text style={{ fontSize: 17, marginHorizontal: 10, marginTop: 5, color: 'grey' }}>
{item.phoneNumbers && item.phoneNumbers[0] && item.phoneNumbers[0].number}
</Text>
</View>
<View style={{ flex: 1, borderTopWidth: 0.5, borderTopColor: 'grey' }}>
{itemChecked.includes(item.phoneNumbers[0].digits) === false ? (
<CheckBox
style={{ width: 15, height: 15 }}
right={true}
checked={false}
onPress={() => {
onChangeValue(item, index);
}}
/>
) : (
<CheckBox
style={{ width: 15, height: 15, paddingTop: 8 }}
right={true}
checked={true}
onPress={() => {
onChangeValue(item, index);
}}
/>
)}
</View>
{/* <Post postJson={item} isGroupAdmin={isGroupAdmin} user={user} /> */}
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<View
style={{
height: 40,
justifyContent: 'center',
backgroundColor: '#eeeeee',
width: '90%',
marginHorizontal: 20,
marginTop: 15,
borderRadius: 10,
}}
>
<Feather name="search" size={20} color="grey" style={{ position: 'absolute', left: 32 }} />
<TextInput
placeholder="Search"
placeholderTextColor="#949494"
style={{
left: 20,
paddingHorizontal: 35,
fontSize: 20,
}}
value={search}
onChangeText={(text) => {
searchFilter(text);
setSearch(text);
}}
/>
</View>
<FlatList
style={{ marginTop: 15 }}
data={contacts && filter}
keyExtractor={(item) => `key-${item.id.toString()}`}
renderItem={renderItem}
refreshing={true}
initialNumToRender={10}
ListEmptyComponent={<Text message="No contacts found." />}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
export default ContactList;

react native: change style property on scroll scrollview

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>
);
};

React Native - Image bigger than parent

Like in tinder, i have a screen with 3 Image Pickers. When I pick an image from the gallery, I want it to persist inside the TouchableOpacity container. However, the image overlaps the container.
I have been been trying various things mentioned here but the image still overlaps like in the image below:
What is the problem?
/* eslint-disable react-native/no-inline-styles */
import React, { useState } from 'react'
import {
Text,
View,
Image,
Button,
TouchableOpacity,
StyleSheet,
Dimensions,
} from 'react-native'
import ImagePicker from 'react-native-image-crop-picker'
import { FontAwesomeIcon } from '#fortawesome/react-native-fontawesome'
import { faPlusCircle } from '#fortawesome/free-solid-svg-icons'
import Colors from '../../../constants/Colors'
const { width: WIDTH } = Dimensions.get('window')
const ProfileEdit = () => {
const [photo, setPhoto] = useState(null)
const handleChoosePhoto = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
}).then((image) => {
setPhoto({
uri: image.path,
width: image.width,
height: image.height,
mime: image.mime,
})
console.log(image)
})
}
return (
<View style={styles.main}>
<View style={styles.button_row}>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
{photo ? (
<Image source={photo} style={styles.photo} />
) : (
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
)}
</TouchableOpacity>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
</TouchableOpacity>
</View>
<View style={styles.buttonWrapper}>
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
</TouchableOpacity>
</View>
</View>
</View>
)
}
const styles = StyleSheet.create({
main: {
flex: 1,
},
icon: {},
button: {
height: `${100}%`,
backgroundColor: Colors.defaultWhite,
padding: 2,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 3,
},
buttonWrapper: {
width: WIDTH / 3,
padding: 10,
},
button_row: {
flex: 0.3,
flexWrap: 'wrap',
flexDirection: 'row',
},
photo: {
width: WIDTH / 2,
height: WIDTH / 2,
// borderRadius: 3,
resizeMode: 'contain',
},
})
export default ProfileEdit
correct answer as described here
"However because images will try to set the width and height based on the actual size of the image, you need to override these style properties"
<Image
style={{
flex: 1,
alignSelf: 'stretch',
width: undefined,
height: undefined
}}
source={require('../../assets/images/onboarding-how-it-works.png')}
/>
Instead of calculating image width try this:
<TouchableOpacity
onPress={() => handleChoosePhoto()}
style={styles.button}>
{photo ? (
<View style={styles.photoContainer}>
<Image source={photo} style={styles.photo} />
</View>
) : (
<FontAwesomeIcon
icon={faPlusCircle}
color={Colors.defaultColor}
size={22}
style={styles.icon}
/>
)}
</TouchableOpacity>
const styles = StyleSheet.create({
photoContainer: {
// flex: 1
},
photo: {
height: WIDTH / 2,
width: '100%',
resizeMode: 'contain'
}
});
I prefer ImageBackground instead. Below is the code to achieve you the desired View:
<ImageBackground
imageStyle={styles.image}
style={styles.imageContainer}
source={{uri: this.state.imageUri}}>
<TouchableOpacity onPress={this._pickImage} style={{
height: WIDTH/2,
width: WIDTH/2,
justifyContent: "center",
alignItems: "center"}}>
<Entypo name="camera" color={Colors.GREY} size={35}/>
</TouchableOpacity>
</ImageBackground>

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