How to display a button at the bottom of a Webview in react-native? - react-native

Inside my component (PrivacyPolicy.js), i have a header view, a webview, and a footer view. the webview, depending on the size, gets scrollable. my issue is that the footer view is displayed at the bottom of the screen like if its style was "position: 'absolute'" so it keeps displayed while scrolling. I need to have it after all webview is displayed.
<View style={styles.main_container}>
<View style={styles.header_container}>
...
</View>
<WebView originWhitelist={['*']} source={{ html: privacyPolicyContent }}/>
<View style={styles.footer_container}>
<CheckBox
disabled={false}
value={this.state.isChecked}
onValueChange={(newValue) => this.setState({
isChecked: newValue
})}
style={styles.checkbox}
tintColors={{ true: '#157dfa' }}
/>
<Text style={styles.checkbox_text}>I have read and accept the Privacy Polic</Text>
</View>
</View>
My styles:
const styles = StyleSheet.create({
main_container: {
flex: 1,
paddingHorizontal:'5%'
},
header_container: {
height: scale(90),
flexDirection: 'row',
marginLeft: 10
},
checkbox_container: {
flexDirection: 'row'
},
checkbox: {
marginLeft: -5,
},
checkbox_text: {
marginTop: 8,
fontSize: 10
}
})

I can see few suggestions:
Since your button is a React Native Button => You can show/hide based on the scrollY positions. For that, you need to communicate over the Bridge to dispatch an event accordingly.
As an alternative solution => You can create the button on the Webview its self to have the same functionality.

Related

Handle on press in a react-native custom component

I'm using react-native. I need to show a menu that consists of a list of items. I simplified the code below and I'm showing only a text but in the real case, the item has got a checkbox an icon and a label so for this reason I created a custom component.
I need to handle the on click on the items.
The problem is that the on press on the TouchableOpacity in the widget is not triggered.
If I use the same hierarchy of components directly the on click works.
So in the example below the onClick on the TouchableOpacity that is directly put in the containing view is triggered but the onClick on the TouchableOpacity that is wrapped in the EventOnMapMenuWidget is not triggered.
How can I handle the onClick on the TouchableOpacity in the EventOnMapMenuWidget?
Please notice that in the log I don't see the
EventOnMapMenuWidget onPress
while I see the
TEST onPress
private drawEventsOnMapForm() {
return <View style={{ margin: marginStyle.medium, flexDirection: 'column' }}>
<EventOnMapMenuWidget
onToggle={() => {
console.log("EventOnMapMenuWidget onToggle")
}}
label={STRINGS.speeding}
/>
<TouchableOpacity style={{ margin: marginStyle.medium, flexDirection: 'row' }} onPress={() => {
console.log("TEST onPress")
}}>
<Text style={{ color: COLORS.black }}>{STRINGS.speeding}</Text>
</TouchableOpacity>
</View>
}
This is the definition of the custom components:
export default class EventOnMapMenuWidget extends Component<EventOnMapMenuWidgetProp> {
render() {
return <TouchableOpacity style={{ margin: marginStyle.medium, flexDirection: 'row' }} onPress={()=> {
console.log("EventOnMapMenuWidget onPress", this.props.label)
this.props.onToggle()
}}>
<Text style={{color: COLORS.black}}>{this.props.label}</Text>
</TouchableOpacity>
}
}

Navigation using images nested inside Touchable Opacity

Background:
I've designed a custom footer for my app in React Native, I've set some images to act as icons. I'm trying to have them redirect to other pages of the app upon touch.
What I have tried
I've been trying to use the same images nested within TouchableOpacity components to have them redirect to other pages using react navigation.
This is my code:
export class Footer extends React.Component {
render (){
return (
<View style = { styles.footStyle } >
<TouchableOpacity onPress={ () => navigation.push('Home')} >
<Image
style = { styles.iconStyle }
source = {require('./img/home.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Favoritos')} >
<Image
style = { styles.iconStyle }
source = {require('./img/heart.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Search')} >
<Image
style = { styles.iconStyle }
source = {require('./img/search.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Notifications')} >
<Image
style = { styles.iconStyle }
source = {require('./img/bell.png')}/>
</TouchableOpacity>
<TouchableOpacity onPress={ () => navigation.push('Help')} >
<Image
style = { styles.iconStyle }
source = {require('./img/circle.png')}/>
</TouchableOpacity>
</View>
)
}
}
const styles = StyleSheet.create({
footStyle: {
paddingBottom: 0,
paddingRight: 10,
backgroundColor: '#ffffff',
flex: 0.4,
flexDirection: 'row',
borderTopWidth: 1,
borderTopColor: '#000000'
},
iconStyle: {
flex: 0.2,
height: undefined,
width: undefined
}
})
Problem
When I try and run the app in expo, the images are not rendering at all. I get my blank footer without any content. I've tried touching the footer to see if the images weren't rendering but the "button" actually worked, that didn't work.
Question
How exactly can I nest an image within a TouchableOpacity component? Is it even possible to use this method with React Navigation?
Thanks a lot!
For an Image component to work you should provide a height and width in style.
Here you are setting it as undefined
Try something like
iconStyle: {
flex: 0.2,
height: 100,
width: 100
}
Also on the navigation, you will have to pass the navigation prop to the Footer. As its a class you should access it as this.props.navigation.navigate()
As your code for integrating the Footer is not here, its hard to comment on how to pass the prop to the footer.

Android: React Native Overlap TouchableOpacity and View behave differently when there is backgroundColor style

I created 2 Views that display overlaps to each others. The top and the bottom
When the bottom view background wasn't configured. It responded to the press event correctly. Let's say when I press on the overlap zone, it showed that the bottom one had been pressed
However, when I configured the bottom view backgroundColor. When I pressed on the overlap zone, on Android, it responded as I pressed on the top view which I think it's incorrect. (iOS it responded correctly that the bottom was pressed)
Steps To Reproduce
Provide a detailed list of steps that reproduce the issue.
Here is an example component
const OverlapseTouchExample = ({backgroundColor}) => {
const [pressedBox, setPressefBox] = React.useState('')
return (
<View>
<Text>{pressedBox} pressed</Text>
<TouchableOpacity style={[styles.box, {backgroundColor: 'blue'}]} onPress={() => setPressefBox('top')} />
<View style={backgroundColor ? { backgroundColor: 'orange' } : null}>
<View style={{marginTop: -75}}>
<TouchableOpacity style={[styles.boxBottom, backgroundColor ? { backgroundColor: 'green '} : null]} onPress={() => setPressefBox('bottom')} />
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
box: {
width: 150,
height: 150,
borderWidth: 1,
},
boxBottom: {
width: 120,
height: 200,
borderWidth: 1,
}
})
The problem found when set the backgroundColor to true
<OverlapseTouchExample backgroundColor={true} />
You could see it in Snack https://snack.expo.io/#gie3d/9b6c32 (Android)

React Native FlatList rendering a few items at a time

I have a list of chat messages in my app to which new items are added to the bottom. I used some code from another SO question to make the FlatList stick to the bottom when new items are added, as below
<FlatList
data={messages}
renderItem={({item}) => <ChatMessage message={item}></ChatMessage>}
keyExtractor={(item, index) => index.toString()}
initialNumToRender={messages.length}
initialScrollIndex={messages.length-1}
ref={ref => this.flatList = ref}
onContentSizeChange={(contentWidth, contentHeight)=>{
this.flatList.scrollToEnd();
}}
/>
The problem is that when the initial list renders (only 35 items, hardcoded in an array for now) it seems to render just a few items, then scroll down a bit, then render a few more, then scroll down a bit until it finally completes the rendering and sticks to the bottom. It's choppy and slow, despite adding initialNumToRender={messages.length} and rendering an incredibly simple node for each result.
Ideally I guess I need to wait for it to fully render before displaying anything to the user but (A) they'd have to wait a couple of seconds to start using the chat room and (B) I don't think that's how Flatlist works, I assume the elements have to be viewable before it is rendered.
Is there just a better way to do this? (Testing on Android by the way)
EDIT: Adding ChatMessage component for completeness
// Chat Message
import React, { Component } from 'react'
import {
StyleSheet,
ImageBackground,
Text,
View
} from 'react-native'
class ChatMessage extends Component {
constructor(props) {
super(props)
this.state = { }
}
render() {
return (
<View style={styles.chatMessage}>
<View style={styles.chatMessage_layout}>
<View style={styles.chatMessage_pic}>
<View style={styles.chatMessage_pic_image}>
<ImageBackground
source={require('./assets/images/profile-pics/example-profilr.png')}
style={styles.chatMessage_pic_image_background}
imageStyle={{ borderRadius: 40/2 }}
resizeMode="cover"
>
</ImageBackground>
</View>
</View>
<View style={styles.chatMessage_details}>
<View style={styles.chatMessage_name}>
<Text style={styles.chatMessage_name_text}>
{this.props.message.name}
<Text style={styles.chatMessage_name_time}> 24h</Text>
</Text>
</View>
<View style={styles.chatMessage_message}>
<Text style={styles.chatMessage_message_text}>{this.props.message.text}</Text>
</View>
</View>
</View>
</View>
)
}
}
export default ChatMessage;
const styles = StyleSheet.create({
chatMessage: {
paddingVertical: 10,
paddingHorizontal: 24
},
chatMessage_layout: {
flexDirection: 'row'
},
chatMessage_pic: {
width: 40,
height: 40,
marginRight: 12
},
chatMessage_pic_image: {
width: 40,
height: 40
},
chatMessage_pic_image_background: {
width: 40,
height: 40
},
chatMessage_details: {
flex: 1
},
chatMessage_name_text: {
color: '#FFF',
fontSize: 14,
fontWeight: 'bold'
},
chatMessage_name_time: {
fontSize: 11,
color: 'rgba(255,255,255,0.6)'
},
chatMessage_message: {
flexDirection: 'row',
alignItems: 'center'
},
chatMessage_message_text: {
color: '#FFF',
fontSize: 12
}
})
If you have less number of items and want to render all items at once then you should use ScrollView as mentioned in the docs
ScrollView: Renders all elements at once, but slow if there are large number of elements.
FlatList: Renders items in a lazy mode, when they are about to appear and removes them when they leave the visible display to save memory that makes it usable for performance on large lists.
For Flatlist optimization you need to use PureComponent whenever you render the child so that it only shallow compares the props.
Also in the keyExtractor use a unique id for your item and do not depend upon the index, since when the item updates the index is not reliable and may change

React Native ScrollView snapping back to top after release

I have used ScrollView in other apps adding just a style={styles.container} with the styles. However in this app I am creating in my styles I have alignItems:'flex-start' which throws an error with just style={styles.container} and instead you need to pass in alignItems:'flex-start' through contentContainerStyle={styles.container}.
Error: Invariant Violation: ScrollView child layout (["alignItems"]) must by applied through the contentContainerStyle prop.
However when I add contentContainerStyle when scrolling down in the view, once the finger is lifted off the phone (or release of the mouse in simulator), the scroll automatically goes back to the top. If I just use style={styles.container} and take out the alignItems:'flex-start' it scrolls correctly, but my items in the UI are not laid out how I need them. What is causing it to scroll back to the top with contentContainerStyle and is there a fix?
render:
var _that = this;
var iconsToShow = icons.map(function (icon, i){
if(i < 81){
return (
<TouchableHighlight
onPress={() => _that.changeIcon(indexToChange, icon)}
underlayColor='#F7F7F7'
key={i}>
<Text style={styles.iconText}><IonIcons name={icon} size={30} color="#555" /></Text>
</TouchableHighlight>
);
}
});
return (
<Carousel width={SCREEN_WIDTH} animate={false} indicatorColor="#444" inactiveIndicatorColor="#999" indicatorAtBottom={false} indicatorOffset={16}>
<View>
<ScrollView contentContainerStyle={styles.container}>{iconsToShow}</ScrollView>
</View>
<View>
//next page for carousel
</View>
</Carousel>
);
I figured out how to get it to scroll. Instead of having the View wrapping the ScrollView and the ScrollView having any flex styling or alignItems:'flex-start' with contentContainerStyle={styles.container}, put that on the View which is a child of the ScrollView and just use style= instead of contentContainerStyle=.
render:
<ScrollView style={styles.container}>
<Text style={styles.goalName}>{goal}</Text>
<View style={styles.viewContainer}>
{iconsToShow}
</View>
</ScrollView>
Styling:
var styles = StyleSheet.create({
container: {
backgroundColor: 'transparent',
paddingLeft:20,
paddingRight:20
},
viewContainer:{
flexDirection:'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
flex: 1
},
iconText:{
paddingLeft:15,
paddingRight:15,
paddingTop:15,
paddingBottom:15
},
goalName:{
textAlign:'center',
marginTop:40,
marginBottom:10,
fontSize:20
}
});
If someone still couldn't fix the problem, try put {flex: 1} into "all" parents of the ScrollView