How can I connect 2 different flatlist with same data? - react-native

I'm trying to achieve when I scroll bigger image, green border on smaller ones will go to the next one. I have 2 different flatlist for this. Tried to write item.key to state with onMomentumScrollEnd but didnt work. Here is my
<FlatList
data={this.state.productImage}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
onMomentumScrollEnd={item => { this.setState({ active: item.key });
console.log(item.key) }}
renderItem={({ item }) =>
<View>
<Image source={item.source} style={{ width:
Dimensions.get('window').width, height:
Dimensions.get('window').height / 2 }} />
</View>}
keyExtractor={item => item.key}
/>
<FlatList
data={this.state.productImage}
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ flexDirection: "row", alignItems: "center",
marginHorizontal: theme.SIZE.pageMargin, marginVertical: 20 }}
renderItem={({ item }) =>
<TouchableOpacity >
<Image source={item.source} style={[this.state.active === item.key
&& styles.activeImage, styles.productImages]} />
</TouchableOpacity>}
keyExtractor={item => item.key}
/>
Edit: After fighting with this I've solved
onMomentumScrollEnd={(event) => {
let xPosition = Math.round(event.nativeEvent.contentOffset.x)
let imageWidth = Math.round(Dimensions.get('window').width)
let keyOfShownImg = (Math.round((xPosition + imageWidth) / imageWidth))
this.setState({ active: keyOfShownImg })
}}

Actually you nearly got the solution. Here you can use onViewableItemChanged prop to achieve this.
I tried out the solution. Take a look here
import * as React from 'react';
import { Text, View, StyleSheet, FlatList, Image } from 'react-native';
import Constants from 'expo-constants';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visibleIndex: 0,
data: [
'https://homepages.cae.wisc.edu/~ece533/images/airplane.png',
'https://homepages.cae.wisc.edu/~ece533/images/arctichare.png',
'https://homepages.cae.wisc.edu/~ece533/images/arctichare.png',
'https://homepages.cae.wisc.edu/~ece533/images/arctichare.png',
],
};
this.onViewableItemsChanged = this.onViewableItemsChanged.bind(this);
}
renderBigList = ({ item, index }) => {
return (
<Image
style={{ width: 300, height: 300, resizeMode: 'contain' }}
source={{ uri: item }}
/>
);
};
renderSmallList = ({ item, index }) => {
return (
<View
style={{
borderColor:
index === this.state.visibleIndex ? 'green' : 'transparent',
borderRadius: 10,
borderWidth: 2,
margin: 3,
}}>
<Image
style={{ width: 100, height: 100, resizeMode: 'contain' }}
source={{ uri: item }}
/>
</View>
);
};
onViewableItemsChanged = (viewableItemData, changed) => {
//console.log(viewableItemData);
if (viewableItemData.viewableItems.length) {
let keyOfVisibleItem = viewableItemData.viewableItems[0].index;
console.log(keyOfVisibleItem);
this.smallListRef.scrollToIndex({
index: keyOfVisibleItem,
animated: true,
});
this.setState({ visibleIndex: keyOfVisibleItem });
}
};
render() {
return (
<View style={styles.container}>
<FlatList
keyExtractor={(item, index) => '' + index}
style={{ height: '50%' }}
data={this.state.data}
extraData={this.state}
renderItem={this.renderBigList}
horizontal={true}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={{
itemVisiblePercentThreshold: 90,
}}
showsHorizontalScrollIndicator={false}
/>
<FlatList
ref={ref => (this.smallListRef = ref)}
keyExtractor={(item, index) => '' + item}
style={{ height: 50 }}
data={this.state.data}
extraData={this.state}
renderItem={this.renderSmallList}
horizontal={true}
showsHorizontalScrollIndicator={false}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
padding: 8,
backgroundColor: 'white',
},
});

Related

Force BottomSheetFlatList open from first position every time useCallback called in react-native-bottom-sheet

I am using react-native-bottom-sheet with BottomSheetFlatList.
I want to open BottomSheetFlatList at initialPosition every time it opened but I am not able to achieve that. When I open it it's always open at last scrolling position.
<BottomSheet
ref={bottomSheetRef}
index={0}
snapPoints={snapPoints}
enableDismissOnClose={true}
handleIndicatorStyle={{display: 'none'}}
handleStyle={{padding: 0}}>
<View style={{backgroundColor: '#b2b2b2', alignItems: 'center'}}>
{!isOpen && (
<Pressable onPress={() => handleSnapPress(1)}>
<Image
source={IconCollapse}
style={{width: 30, height: 20, backgroundColor: '#b2b2b2'}}
/>
</Pressable>
)}
{isOpen && (
<Pressable onPress={() => handleClosePress()}>
<Image
source={IconExpand}
style={{width: 30, height: 20, backgroundColor: '#b2b2b2'}}
/>
</Pressable>
)}
</View>
<View style={{backgroundColor: '#b2b2b2'}}>
<Text
style={{
color: 'white',
fontSize: 20,
textTransform: 'uppercase',
paddingStart: 10,
}}>
Select Zone
</Text>
<View
style={{
borderBottomColor: '#fff',
borderBottomWidth: 1,
marginEnd: 10,
marginStart: 10,
}}
/>
</View>
<BottomSheetFlatList
data={zoneList}
keyExtractor={(item, index) => `${index}`}
renderItem={renderItem}
contentContainerStyle={styles.contentContainer}
/>
</BottomSheet>
you can try
this.flatListRef.scrollToOffset({ animated: true, offset: 0 });
hope this is work
use Prop enableDismissOnClose
as mentioned here
As Mohammad Momin said you can try scrollToIndex({index: 0, offset: 0}).
In order to do that you have to declare a ref for your FlatList and also specify the getItemLayout prop. Then you can call scrollToIndex.
So every time that you open the bottomSheet you have to call scrollToIndex.
The full working example I created is available in this github repositor which is something like this:
export const CustomBottomSheet = forwardRef(({
zoneList = [],
selectedZone,
onZoneSelected
}, ref) => {
const bottomSheetRef = useRef(null);
const flatListRef = useRef(null);
const scrollToItem = () => {
if (!selectedZone) return;
if (zoneList.length < 1) return;
// find item index
const index = zoneList.findIndex(value => value.id === selectedZone?.id);
// scroll to destination index
// it's better to set animated to true (experimental)
flatListRef.current.scrollToIndex({
animated: true,
index: index, // 0
})
console.log('scrollToItem called by index:' + index)
}
const handleSnapPress = (index = 0) => {
bottomSheetRef.current.snapToIndex(index);
// call this method after each time user opens the bottom sheet
scrollToItem();
}
const handleClosePress = () => {
bottomSheetRef.current.snapToIndex(index);
}
const renderItem = ({ item }) => {
return (
<ListItem
{...item}
onPress={() => onZoneSelected(item)}
isSelected={item.id === selectedZone?.id}
/>
)
}
const getItemLayout = (_, index) => (
{
length: ListItem.HEIGHT,
offset: (ListItem.HEIGHT + ListItem.VERTICAL_SPACING) * index,
index: index,
}
)
// forwarding methods via ref
useImperativeHandle(ref, () => ({
open: handleSnapPress,
close: handleClosePress,
snapToIndex: handleSnapPress,
}));
return (
<BottomSheet
ref={bottomSheetRef}
index={-1}
snapPoints={SNAP_POINTS}
enableDismissOnClose={true}
// onChange={handleOnChange}
enablePanDownToClose={true}
>
<View style={{ backgroundColor: '#b2b2b2', alignItems: 'center', marginTop: 16 }}>
<Text
style={{
color: 'white',
fontSize: 20,
textTransform: 'uppercase',
paddingStart: 10,
}}>
{'Select Zone'}
</Text>
</View>
<BottomSheetFlatList
// add ref and getItemLayout in order to use scrollToIndex method
ref={flatListRef}
getItemLayout={getItemLayout}
data={zoneList}
keyExtractor={(item) => item.id}
renderItem={renderItem}
contentContainerStyle={styles.contentContainer}
/>
</BottomSheet>
)
})

Styling conditional FlatList?

I want to show FlatList only if there are results from an axios call. Unfortunately when there are some results, they are showing beneath the components below it, like TextArea, etc. I tried a lot of styling combinations but nothing works. Any help is appriciated!
const CreateScreen = () => {
const [searchKeyword, setSearchKeyword] = useState("");
const [searchResults, setSearchResults] = useState("");
const [isShowingResults, setIsShowingResults] = useState(false);
searchLocation = async (text) => {
setSearchKeyword(text);
axios
.request({
method: "post",
url:
"https://maps.googleapis.com/maps/api/place/autocomplete/json?key=" +
apiKey +
"&input=" +
searchKeyword +
"&types=(cities)&components=country:bg&language=bg",
})
.then((response) => {
console.log(response.data);
setSearchResults(response.data.predictions);
setIsShowingResults(true);
})
.catch((e) => {
console.log(e.response);
});
};
return (
<ScrollView>
<Text style={{ marginBottom: 3 }}>Address</Text>
<View style={styles.autocompleteContainer}>
<TextInput
returnKeyType="search"
placeholderTextColor="#000"
onChangeText={(text) => searchLocation(text)}
value={searchKeyword}
/>
{isShowingResults && (
<FlatList
data={searchResults}
renderItem={({ item, index }) => {
return (
<TouchableOpacity
style={styles.resultItem}
onPress={() => {
setSearchKeyword(item.description);
setIsShowingResults(false);
}}
>
<Text>{item.description}</Text>
</TouchableOpacity>
);
}}
keyExtractor={(item) => item.id}
style={styles.searchResultsContainer}
/>
)}
</View>
<Text style={{ marginBottom: 3 }}>Title</Text>
<TextInput />
</ScrollView>
);
}
const styles = StyleSheet.create({
autocompleteContainer: {
zIndex: 1,
},
searchResultsContainer: {
width: "100%",
backgroundColor: "#fff",
position: "absolute",
top: 50,
},
resultItem: {
justifyContent: "center",
height: 40,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
paddingLeft: 15,
},
});
{isShowingResults? (
<FlatList
data={searchResults}
renderItem={({ item, index }) => {
return (
<TouchableOpacity
style={styles.resultItem}
onPress={() => {
setSearchKeyword(item.description);
setIsShowingResults(false);
}}
>
<Text>{item.description}</Text>
</TouchableOpacity>
);
}}
keyExtractor={(item) => item.id}
style={styles.searchResultsContainer}
/>
):null}
You can try this, or you can just pass ListEmptyComponent to null, nothing will display when you have empty array in data of flatlist

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

implement tabs in the center of the screen

I am new to react native. i have implemented stacknavigator inside drawernavigator. Using this library
"react-navigation": "^1.0.0-beta.11",
Now i want to implement tabs within the screen at the center. Following image is part of my screen
i dont have any idea how can i do this with any library or manually putting views.
Any help is appreciated.
Thanks
Well, I've solved that scenario using react-native-swiper
Basically you have to wrap all views you want to have inside a Swiper, render and style the header as you want.
Here there is a working example I've made:
render() {
return (
<View style={styles.body}>
<Swiper
height={500}
showsPagination={true}
loop={false}
renderPagination={this._renderPagination}
ref={component => this._swiper = component}>
<View style={styles.page}>
<FlatList data={..} renderItem={item => ...} keyExtractor={(item, index) => index} />
</View>
<View style={styles.page}>
<FlatList data={...} renderItem={item => ...} keyExtractor={(item, index) => index} />
</View>
<View style={styles.page}>
<FlatList data={...} renderItem={item => ...} keyExtractor={(item, index) => index} />
</View>
<View style={styles.page}>
<FlatList data={...} renderItem={item => ...} keyExtractor={(item, index) => index} />
</View>
</Swiper>
</View>
);
}
_renderPagination(index, total, context) {
return (
<View style={styles.pagination}>
{this._renderPaginationHeaders(index, total, context)}
</View>
)
}
_renderPaginationHeaders(index, total, context) {
let ret = [];
for (let i = 0; i < total; i++) {
ret.push(
<TouchableOpacity key={i} style={{ flex: 1, flexDirection: 'column' }} onPress={() => this._onPageChange(i)}>
<Text style={[styles.title, { flex: 1, textAlign: 'center', textAlignVertical: 'center' }]}>
{this._getSectionText(i)}
</Text>
<View style={{ height: 5, backgroundColor: index === i ? 'magenta' : 'transparent' }}></View>
</TouchableOpacity>
);
}
return ret;
}
_onPageChange(targetIndex) {
const currentIndex = this._swiper.state.index;
const offset = targetIndex - currentIndex;
this._swiper.scrollBy(offset);
}
const styles = StyleSheet.create({
body: {
flex: 1,
alignItems: 'center',
alignSelf: 'center',
backgroundColor: '#f1f1f1',
},
pagination: {
flexDirection: 'row',
width: Dimensions.get('window').width,
height: Header.currentHeight * 0.7,
backgroundColor: '#2E2E2E',
paddingLeft: 2,
paddingRight: 2,
alignItems: 'center',
position: 'absolute',
top: 0,
},
page: {
flex: 1,
marginTop: (Header.currentHeight * 0.7) + 3
},
title: {
color: 'white'
},
});