react-native nested ScrollView fails to respond to touch in android - react-native

I'm building a 'Search' component, in which a ScrollView is displayed when the search terms in the TextInput return related tags from the API endpoint. While running in iOS the component works as expected and allows me to scroll through the list and select an option. However, android does not seem to allow scroll or touch events on the ScrollView component. I've decorated the component with the keyboardShouldPersistTaps="always" and nestedScrollEnabled={true}.
Attempted placing the ScrollView at different levels of the overall screen. When close to the top level View, the ScrollView works as intended, but at a certain depth, it ceases to work. Having the ScrollView higher up in the component would require a lot of calculation based on the onLayout events of each nested View in order for the component to match designs, so I'd like to avoid it if possible.
<ScrollView
style={Style.view}
contentContainerStyle={Style.container}
keyboardShouldPersistTaps="handled"
>
<TouchableOpacity
onPress={this.onClose}
style={Style.close}
hitSlop={{
top: 16,
right: 16,
bottom: 16,
left: 16,
}}
>
<Icon
name="times"
size={24}
color={Palette.$color_white}
/>
</TouchableOpacity>
<Animated.View style={[Style.animatedWrapper, { transform: [{ translateY }] }]}>
<View style={Style.tags}>
{
tags.map(tag => (
<View key={tag.guid} style={Style.tag}>
<Strong color={Palette.$color_white}>{tag.name}</Strong>
<TouchableOpacity
style={Style.tagButton}
onPress={() => this.onTagPress(tag)}
hitSlop={{
top: 16,
right: 16,
bottom: 16,
left: 16,
}}
>
<Icon name="times" size={14} color={Palette.$color_white} />
</TouchableOpacity>
</View>
))
}
</View>
<View style={Style.searchWrapper} onLayout={this.onLayout}>
<Image
source={Buzz}
style={Style.illustration}
/>
<View style={Style.inputWrapper}>
<Heading
level={3}
color={Palette.$color_white}
>
Search by key words
</Heading>
<View style={Style.controlWrapper}>
<View style={{ flex: 1 }}>
<TextInput
ref={(ref) => { this.input = ref; }}
returnKeyType="done"
textContentTypez="none"
placeholder="Type your search"
onChangeText={this.onChangeText}
onFocus={this.onFocus}
onBlur={this.onBlur}
underlineColorAndroid="transparent"
placeholderTextColor={Palette.$color_white}
style={Style.searchInput}
/>
<Animated.View
style={[Style.menu, { opacity: menuOpacity }]}
pointerEvents={isMenuOpen ? 'auto' : 'none'}
>
<ScrollView
contentContainerStyle={Style.menuOptionsContentContainer}
keyboardShouldPersistTaps="always"
style={[Style.menuOptions, { maxHeight: scrollHeight }]}
nestedScrollEnabled
>
{
!isLoading
? results.map(tag => (
<TouchableHighlight
style={Style.menuOption}
key={tag.guid}
onPress={() => this.onOptionSelect(tag)}
underlayColor={Palette.$color_light_grey}
>
<Text style={Style.menuOptionText}>{tag.name}</Text>
</TouchableHighlight>
))
: (
<View style={Style.activityIndicator}>
<ActivityIndicator size="small" color={Palette.$color_orange} />
</View>
)
}
</ScrollView>
</Animated.View>
</View>
<StyledTextButton
title="Search"
onPress={this.onSearchPress}
disabled={tags.length <= 0}
/>
</View>
</View>
</View>
</Animated.View>
</ScrollView>
The ScrollView is rendered as expected, but in android it cannot respond to touch and swipe gestures. Works as intended in iOS.
Here's an example in iOS of the Component.

Related

Issue with Formview blocking the main view in react native android application

<View style={styles.bottomContainer} >
<Animated.View style = {buttonAnimatedStyle}>
<Pressable style={styles.button} onPress = {loginHandler} >
<Text style={styles.buttontext} >LOG IN</Text>
</Pressable>
</Animated.View>
<Animated.View style = {buttonAnimatedStyle}>
<Pressable style={styles.button} >
<Text style={styles.buttontext} onPress = {registerHandler} >REGISTER</Text>
</Pressable>
</Animated.View>
<Animated.View style={[styles.inputformcontainer, formAnimatedStyle ]}>
<TextInput placeholder="Email"
placeholderTextColor="#9b1306"
style={styles.textinput}/>
<TextInput placeholder="Full Name"
placeholderTextColor="#9b1306"
style={styles.textinput}/>
<TextInput placeholder="Password"
placeholderTextColor="#9b1306"
style={styles.textinput}/>
<View style = {styles.formButton}>
<Text style={styles.buttontext}> LOG IN </Text>
</View>
</Animated.View>
</View>
Here is the Styling of above views:
bottomContainer: {
justifyContent: 'center',
height: height / 3,
},
inputformcontainer:{
backgroundColor:'grey',
color: '#000',
marginBottom: 70,
...StyleSheet.absoluteFill,
justifyContent : "center",
},
DETAILS OF THE CODE
so basically I am trying to create an aninated view where the "forminput" container should pop up when user taps on log in or register button. But the problem is that my buttons dont work when because the forminput container lands on top of them somehow (eventho it is invisible). The person I am following used
zIndex: -1
to place it underneath the view but it does not work in android.
PS: I have also tried "elevation" Prop but that doest seem to work either.

Styling Two Elements on Same Line in React Native

I am trying to style two elements that need to appear on the same line in my react native app. What I have thus far positions them on the same line - using nested <Text> elements - but I'm unclear how to then be able to create some distance between them. For instance, in the example below, I want the icon to be left-aligned, which it is now, but I want the this.props.client.name text to be center-aligned. From my understanding margin is ignored in children elements. How can I accomplish this?
<View>
<Text>
<Feather name="chevron-left" size={24}
onPress={() => props.navigation.goBack()}
/>
<Text>{this.props.client.name}</Text>
</Text>
</ View>
You can try styling them using "flex" css properties.
for example if you have:
<View>
<Text>
<Feather name="chevron-left" size={24}
onPress={() => props.navigation.goBack()}
/>
<Text>{this.props.client.name}</Text>
</Text>
</ View>
TRY
<View>
<Text>
<View style={{ display: "flex", alignItems: "center",
position: "relative", zIndex: 1 }}>
<Feather name="chevron-left" size={24}
onPress={() => props.navigation.goBack()}
style={{position: "absolute", zIndex: 2, left: 0 }}
/>
<Text>{this.props.client.name}</Text>
</View>
</Text>
</ View>
You should now have your Icon on the left and your text centered.
Read more about Flexbox here. reactnative.dev/docs/flexbox

Is there a way to be able to interact with the background components when Modal is visible?

as the title states, I am attempting to implement a modal in such way that it only takes up some part of the screen so as to allow the user to be able to interact with some of the background components. For reference I am using the react-native-modal library. The current screen looks like this:
I would like to be able to interact with the icons in the header (both are already wrapped in Touchable components so already clickable). However when the Modal is visible I am unable to interact with those.
My implementation is as such:
<Modal
isVisible={showModal}
style={styles.modalContainer}
customBackdrop={<CustomBackdrop />}
onBackdropPress={() => setShowModal(false)}
animationIn="fadeInDown"
animationOut="fadeOutUp"
>
<View style={styles.container}>
<TouchableOpacity style={styles.itemContainer}>
<Text style={styles.label}>A</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.itemContainer}>
<Text style={styles.label}>B</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.itemContainer}>
<Text style={styles.label}>C</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.itemContainer}>
<Text style={styles.label}>D</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.itemContainer,
{ borderBottomLeftRadius: 16, borderBottomRightRadius: 16 },
]}
>
<Text style={styles.label}>E</Text>
</TouchableOpacity>
</View>
</Modal>
And my custom background looks like this:
<View
style={{
marginTop: hp("50%"),
flex: 1,
}}
>
<TouchableWithoutFeedback onPress={() => setShowModal(false)}>
<View
style={{ backgroundColor: "#000", height: hp("50%"), width: wp("100%") }}
/>
</TouchableWithoutFeedback>
</View>

React Native Flatlist ListHeaderComponent Doesn't Work

ListHeaderComponent inside flatlist doesn't work. It won't render nothing in header (above flatlist). If I however put some custom component it does work but it doesn't with my renderHeader function. I tried making a custom component with the same view as in renderHeader but when I used that nothing rendered so I don't know
My flatlist along with whole render:
render() {
const { height } = Dimensions.get("window");
return (
<View style={{flex: 1,
backgroundColor: "#EBECF4"}}>
{/* <Text>
Connection Status :{" "}
{this.state.connection_status ? "Connected" : "Disconnected"}
</Text> */}
{/* <Loader loading={this.state.loading} /> */}
<StatusBar backgroundColor="#63003d" barStyle="light-content" />
<SafeAreaView>
<ModernHeader
backgroundColor="#63003d"
height={50}
text="Eleph"
textStyle={{
color: "white",
fontFamily: "Lobster-Regular",
//fontWeight: "bold",
fontSize: 25,
}}
leftIconComponent={
<TouchableOpacity
style={{ marginRight: 0, margin: 0 }}
onPress={() =>
this.props.navigation.dispatch(DrawerActions.openDrawer())
}
>
<FontAwesome5 name="bars" size={22} color="white" />
</TouchableOpacity>
}
rightIconComponent={
<TouchableOpacity
style={{ marginLeft: 0, margin: 0 }}
onPress={() => this.props.navigation.navigate("Profile")}
>
{/* <MaterialCommunityIcons
name="chart-areaspline"
size={24}
color="#639dff"
/> */}
<Foundation name="graph-bar" size={24} color="white" />
{/* <FontAwesome name="bar-chart" size={24} color="white" /> */}
</TouchableOpacity>
}
/>
</SafeAreaView>
<FlatList
//contentInset={{ top: HEADER_HEIGHT }}
//contentOffset={{ y: -HEADER_HEIGHT }}
data={this.state.post}
extraData={this.state.post}
keyExtractor={(item) => item.id.toString()}
style={{
marginHorizontal: 0,
marginVertical: 6,
}}
renderItem={({ item }) => this.renderPost(item)}
ListFooterComponent={this.renderFooter}
ListHeaderComponent={this.renderHeader}
refreshControl={
<RefreshControl
style={{ color: "#639dff" }}
tintColor="#639dff"
titleColor="#fff"
refreshing={this.state.isLoading}
onRefresh={this.onRefresh}
/>
}
initialNumToRender={7}
onEndReachedThreshold={0.1}
showsVerticalScrollIndicator={false}
onEndReached={() => {
if (
!this.onEndReachedCalledDuringMomentum &&
!this.state.isMoreLoading
) {
this.getMore();
}
}}
onMomentumScrollBegin={() => {
this.onEndReachedCalledDuringMomentum = false;
}}
onScroll={(e) => {
scrollY.setValue(e.nativeEvent.contentOffset.y);
}}
></FlatList>
{/* <View style={styles.footer}>
<TouchableOpacity
activeOpacity={0.9}
onPress={this.getMore}
style={styles.loadMoreBtn}
>
<Text style={styles.btnText}>See more moods</Text>
{this.state.loading ? (
<ActivityIndicator color="white" style={{ marginLeft:8 }} />
) : null}
</TouchableOpacity>
</View> */}
</SafeAreaView>
</View>
My renderHeader function:
renderHeader = () => {
return (
<View
style={{
flex: 1,
flexDirection: "row",
backgroundColor: "#ffffff",
//width: "100%",
padding: 11,
alignItems: "center",
position: "absolute",
justifyContent: "center",
//top: 50,
//bottom: 0,
//left: 0,
//elevation: 4,
//right: 0,
//height: 50,
}}
//flexDirection: "row",
//backgroundColor: "#ffffff",
//width: "100%",
//padding: 11,
//alignItems: "center",
>
<TouchableOpacity
onPress={() => this.props.navigation.navigate("Post")}
>
<Text
style={{ alignItems: "center", color: "#C4C6CE" }}
//placeholder={How are you feeling right now ${this.state.user.name}?}
multiline
>{Tell us how are you feeling right now ${this.state.user.name}?}</Text>
</TouchableOpacity>
{/* <View style={styles.divider}></View> */}
</View>
);
}
Why for example renderFooter works??
renderFooter = () => {
if (!this.state.isMoreLoading) return true;
return (
<ActivityIndicator
size="large"
color={"#D83E64"}
style={{ marginBottom: 10 }}
/>
);
};
Use ListHeaderComponentStyle to style the ListHeaderComponent.
I created a simple snack for you, check if this helps. Your code looks okay but not sure about the whole screen design. Here is my snack link.
https://snack.expo.io/#saad-bashar/excited-fudge
It might be related to the other components inside your screen. So first check only with flatlist, remove all other components. Hope you can sort it out :)
The solution is to remove the "absolute" style in the header for flatlist. The question is mine im just answering with different account but this is the solution that worked.

Horizontal Flatlist Re-Render changes ListHeaderComponent

I'm banging my head on this one. I am using a horizontal flat list. And putting a view in ListHeaderComponent to act as padding. You can see it as a blue box in the first image below. That works great. Trouble is, when I refresh the flatlist from elsewhere in my app, the padding seems to break. See the blue box in image 2 below. Code below images. Any help appreciated.
OnLoad:
After props refresh:
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
horizontal={true}
showsHorizontalScrollIndicator={false}
data={this.props.store.allBookData}
ListHeaderComponent={<View style={{ width: itemOffset / 2, height: 200, backgroundColor: 'blue' }}></View>}
ListFooterComponent={<View style={{ width: itemOffset /2 }}></View>}
renderItem={({ item }) =>(
<TouchableWithoutFeedback onPress={() => this.gotoBook(item)}>
<Animatable.View
animation={"fadeIn"}
easing={"ease-in-out"}
duration={320}
delay={240}
useNativeDriver={true}
style={[styles.bookItem, { width: itemWidth, height: itemHeight, marginTop: itemMarginTop, opacity: 1, marginRight: 24 }]}>
{/* cover */}
<View style={[styles.bookCover, { width: itemWidth, height: bookHeight, backgroundColor: global.setBookItemColor(item.color)}]}>
<View style={styles.bookBindingHolder}>
<View style={styles.bookBinding}></View>
</View>
<View style={styles.bookCoverPhotoHolder}>
<View style={styles.bookCoverPhoto}>
<FastImage style={styles.bookCoverFrame} resizeMode={'contain'} source={Images.coverFrameWithTape}/>
{(() => {
//display cover
if (item.cover == 'temp') {
//temp
return (<FastImage style={styles.bookCoverImg} resizeMode={'cover'} source={Images.tempCover} />)
}else{
//photo
return (
<FastImage
style={[styles.bookCoverImg, {opacity: 0.88}]}
resizeMode={'cover'}
source={{uri: item.cover}}>
</FastImage>
)
}
})()}
</View>
</View>
</View>
{/* title */}
<View style={styles.bookTitleHolder}>
<Text style={[global.TextStyles.h3, global.TextStyles.darkText, global.TextStyles.alignCenter]}>{item.title}</Text>
</View>
{/* count */}
<View style={styles.bookPhotoCountHolder}>
<View style={styles.bookPhotoCount}>
{(() => {
if (item.photoCount == global.maxPhotos){
var itemCount = 'Full'
}else{
var itemCount = item.photoCount + '/' + global.maxPhotos
}
return(
<Text style={[global.TextStyles.lightText, global.TextStyles.smallerLabel]}>{itemCount}</Text>
)
})()}
</View>
</View>
</Animatable.View>
</TouchableWithoutFeedback>
)}/>
It's hard to say where the problem comes from even though there's a good chance it comes from your itemOffset that you apply as width to your HeaderComponent.
However, I have the impression that you can make it easier to define paddings, instead of defining a header and footer component, you could simply apply paddings to the content of your list by passing to your list the props: contentContainerStyle
like that
<FlastList
...
contentContainerStyle={{
paddingHorizontal: itemOffset / 2
}}
/>
but I think you should look in your calcul of itemOffset