import React, { useState } from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { BottomSheet } from "react-native-btr";
export default function BottomSheetDemo() {
const [visible, setVisible] = useState(false);
function toggle() {
setVisible((visible) => !visible);
}
return (
<View style={styles.container}>
<TouchableOpacity onPress={toggle}>
<View style={styles.button}>
<Text>Toggle BottomSheet</Text>
</View>
</TouchableOpacity>
<BottomSheet
visible={visible}
onBackButtonPress={toggle}
onBackdropPress={toggle}
>
<View style={styles.card}>
<Text>Place your custom view inside BottomSheet</Text>
</View>
</BottomSheet>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
button: {
backgroundColor: "#fff",
borderWidth: 2,
borderRadius: 50,
padding: 16,
},
card: {
backgroundColor: "#fff",
height: 250,
justifyContent: "center",
alignItems: "center",
},
});
I am using the above code. So my problem is I am getting the bottom sheet as expected. And I want that it will 100px above the bottom. That is also working fine. But the thing is I am getting bottom and at top blur black background. But I want on the bottom margin-bottom 100. It will not show that black ground. I will start from marginbottom:100. But the bottom background will be transparent and I can do click on the bottom items.
TLDR: working source code. Be aware, this solution might get
messy.
If we see the source code of react-native-btr then we will see that the BottomSheet component is actually a Modal component from react-native-modal passing 4 basic props to react-native-modal as shown below
<Modal
isVisible={visible}
onBackButtonPress={onBackButtonPress}
onBackdropPress={onBackdropPress}
style={{ justifyContent: "flex-end", margin: 0 }}
>
{children}
</Modal>
So, what I did is, extract all the source code from react-native-modal which is just five files and modify this source code. I have put a new prop for react-native-modal called bottomSpacing, so that user can change the bottom spacing.
Back in app.js the code is looking like this
<Modal
testID={"modal"}
isVisible={isModalVisible}
style={{ justifyContent: "flex-end", margin: 0 }} // this line was using in react-native-btr source
bottomSpacing={BOTTOM_SPACING}>
<View style={styles.card}>
<Text>Place your custom view inside BottomSheet</Text>
</View>
</Modal>
Here, BOTTOM_SPACING is used both in bottomSpacing props and the styles.card as following
card: {
marginBottom: BOTTOM_SPACING,
...
}
Bonus: Now you can use all the features of react-native-modal such as changing the color of the shadow and the opacity of that shadow etc.
I have a modal that I want show when the user clicks a i button.
The modal envelops the entire screen, by setting the position to 'absolute'. This works fine, but when the user clicks somewhere on the screen, it should disappear again.
My TouchAbleWithoutFeedback however, does not register a press. It appears to be having something to do with the position being absolute but I can't put my finger on it.
Also, the clickable elements underneath the modal are still interactable, even though the modal is displayed on top of those elements.
Here is my code:
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
render()
{
return (<View>
<View style={styles.container}>
<OtherElements/>
</View>
<MainScreenInfoModal showModal={this.state.showInfoModal} />
</View>);
}
const MainScreenInfoModal = ({showModal}) =>
{
if (showModal)
{
return <TouchableWithoutFeedback style={{zIndex: 10}} width={window.width} height={window.height} onPress={() => {console.log('press!')}}>
<View style={styles.infoModal} >
<SvgXml width={300} height={300} xml={mainscreenInfomodal} style={{
alignSelf: 'center',
marginTop: window.height * .45
}}/>
</View>
</TouchableWithoutFeedback>
}
return null;
}
const styles = StyleSheet.create({
container:
{
fontSize: 26,
textAlign: 'center',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-start',
alignItems: 'center',
padding: 13,
fontWeight: "bold",
},
{...} ,
infoModal:
{
position: 'absolute',
bottom: 0,
width: window.width,
height: window.height,
backgroundColor: 'rgba(255,255,255,0.7)',
zIndex: 10
}});
try react-native-modalize library, it saved me alot of time and nerves developing modals
A have two layers with components on the screen. Upper layer is intercepting all touch events. I need the way to propagate those events to the below layer. Please, look the example below, for now only onUpperViewPressed is outputs. The desired behavior are both: onBellowViewPressed and onUpperViewPressed should be outputed when corresponding views are pressed.
import * as React from 'react';
import { Text, View, SafeAreaView } from 'react-native';
export default function App() {
return (
<SafeAreaView style={{flex: 1}}>
<View style={{ flex: 1, backgroundColor: 'grey' }}>
<Text
onPress={() => console.log('onBellowViewPressed')}
style={{
position: 'absolute',
top: 100,
left: 100,
backgroundColor: 'red',
}}>
Below view
</Text>
<Text
onPress={() => console.log('onUpperViewPressed')}
style={{ flex: 1 }}>
Upper view
</Text>
</View>
</SafeAreaView>
);
}
https://snack.expo.io/jJ1DQDMeR
What I've already tried:
Tried to add 'zIndex: 1' to 'below view', but in that case 'below view' lower view starts to intercept all events.
Tried to play with pointerEvents with no luck.
Also tried react-native-touch-through-view
Any suggestions? Thank you.
I would like to create a screen with a ScrollView with some input fields in it, and a Button (actually 2 buttons in a normal View) at the bottom of the screen. This should be pretty easy, BUT, I want to position my button at the very bottom of the screen even if there isn't enough element in the ScrollView to fill the whole screen. I could use absolute positioning, BUT my ScrollView can be bigger (higher) then my screen, and in this case, the button should be at the end of the ScrollView. (So it would be out of the screen, which means the user should scoll down to see the button).
I've tried a lot of things, but the Button always comes right after the other elements inside the ScollView.
In the picture the scrollview has a blue border, and the normal view which contains the buttons has black border.
Any suggestion is appreciated.
Found the best solution.
The key is the contentContainerStyle property (doc: https://facebook.github.io/react-native/docs/scrollview#contentcontainerstyle). "These styles will be applied to the scroll view content container which wraps all of the child views. "
After setting flexGrow: 1, justifyContent: 'space-between', flexDirection: 'column' in contentContainerStyle, one can set justifyContent: 'flex-start' for the upper container view with the text, and justifyContent: 'flex-end' for the lower container view with the buttons.
<ScrollView
contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between', flexDirection: 'column' }}
style={{ backgroundColor: 'white', paddingBottom: 20 }}
>
<View style={{ flex: 1, justifyContent: 'flex-start' }}>
<Text>Some text with different length in different cases, or some input fileds.</Text>
</View>
<View style={{ flex: 1, justifyContent: 'flex-end' }}>
<View>
<TouchableOpacity style={[commonStyles.greenButton, styles.buttonPadding]}>
<Text style={commonStyles.greenButtonTitle}>Next</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity style={styles.cancelButtonContainer}>
<Text style={styles.cancelButton}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>;
<ScrollView
contentContainerStyle={{
flexGrow: 1,
justifyContent: 'flex-end',
}}>
</ScrollView>
I found a workaround for this.
The main idea is to set the height of the View with the texts and input fields to match exactly the height of the screen minus the View which contains the buttons.
The challenge is to calculate this height. Abhishek Kumar's answer helped me (https://stackoverflow.com/a/41398953/4109477) See my code below:
import { View, ScrollView, Text, TouchableOpacity, Dimensions } from 'react-native';
const screenHeight = Dimensions.get('window').height;
class MyClass extends React.Component {
constructor(props) {
super(props);
this.state = {buttonViewHeight: 0};
}
find_dimesions(layout){
this.setState({buttonViewHeight: layout.height});
}
render() {
return (
<View style={{minHeight: screenHeight, flex: 1, alignItems: 'center'}}>
<ScrollView style={{minHeight: screenHeight}}>
<View style={{minHeight: screenHeight}}>
<View style={{minHeight: screenHeight - this.state.buttonViewHeight, borderWidth: 2, borderColor: 'red', alignItems: 'center', flex: 1}]}>
<Text>Some text with different length in different cases, or some input fileds.</Text>
</View>
<View onLayout={(event) => { this.find_dimesions(event.nativeEvent.layout) }} style={{paddingBottom: 50, flex: 1, borderWidth: 2, borderColor: 'green'}}>
<TouchableOpacity
style={[commonStyles.greenButton, styles.buttonPadding]} >
<Text style={commonStyles.greenButtonTitle}>Next</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.cancelButtonContainer} >
<Text style={styles.cancelButton}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView>
</View>
}
}
On the picture below you can see the result
This solution will cause an animation like effect as the view renders multiple times, because of the changing of the View's height. The button's View "fade in" as you open the screen.
If you find a better solution please let me know!
I have a Touchablehighlight that I need to position absolute, but it becomes unclickable after I do it.
What could cause this? It functions like it should if I dont have the position set to absolute.
Solution was to change the order of the components.
What i originally had:
<TouchableHighLight><Text>Click me</Text></TouchableHighlight>
<View> .... </View>
This was the fix:
<View>...</View>
<TouchableHighLight><Text>Click me</Text></TouchableHighlight>
Dude just go and add zIndex : 1 to the view containing the buttons and boom you are done in most of the cases. Also note adding elevation adds shadow to android button and sometimes elevation may also be a issue if its added to parent and not added to child then the child button may not work.(Rare Case)
eg:
buttonContainers:
{
zIndex: 1,
alignSelf: 'flex-end',
position: 'absolute',
top:5,
right: 5,
height: 40,
borderWidth: 1,
justifyContent: 'center',
alignContent: 'center',
width: 80
},
SOLVED:
I faced this issue today. I have solved it.
Import TouchableOpacity from react-native-gesture-handler instead of react-native.
Before:
import {TouchableOpacity} from "react-native";
After:
import {TouchableOpacity} from 'react-native-gesture-handler'
use onPressIn instead of onPress
That made the area clickable!
I used TouchableOpacity inside an absolute view. The onPress function was not called after press it. But the opacity changed. I've tried all the above solutions, but none works.
My solutions is use onPressIn instead of onPress.
It seems like the inner action of Touchable* is weird in ReactNative when it's in an absolute view.
After trying everything for two hours, the solution I found was to change my button position.
Before ...
export default class Navbar extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
return (
<View style={styles.content}>
<TouchableOpacity
onPress={this.props.openModal}
style={styles.containerButton}
>
<Text>New</Text>
</TouchableOpacity>
<Text style={styles.textCenter}>Remember me</Text>
</View>
);
}
}
const styles = StyleSheet.create({
content: {
paddingTop: 30,
paddingBottom: 10,
backgroundColor: '#81C04D',
flexDirection: 'row'
},
containerButton: {
position: 'absolute',
top: 30,
left: 8
},
textCenter: {
flex: 1,
textAlign: 'center',
fontWeight: 'bold'
}
});
After ...
export default class Navbar extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
return (
<View style={styles.content}>
<Text style={styles.textCenter}>Remember me</Text>
<TouchableOpacity
onPress={this.props.openModal}
style={styles.containerButton}
>
<Text>New</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
content: {
paddingTop: 30,
paddingBottom: 10,
backgroundColor: '#81C04D',
flexDirection: 'row'
},
containerButton: {
position: 'absolute',
top: 30,
left: 8
},
textCenter: {
flex: 1,
textAlign: 'center',
fontWeight: 'bold'
}
});
It works!!!
My solutions is:
style:{
zIndex: 1,
position: 'absolute',
}
use
zIndex: 1 in view, it'll work.
<View style={{position : 'absolute', marginTop : 25, zIndex: 1}}>
More details can be found here :
How to use zIndex in react-native
For me, it works like:
import { TouchableOpacity } from 'react-native';
onPress, zIndex: 1, position: 'absolute'
One more solution.....
For me what worked was a combination of things....
import { TouchableOpacity } from 'react-native-gesture-handler'
and I WRAPPED my TouchableOpacity in a View.
before:
<TouchableOpacity onPress={()=> addCallback()}
style={styles.addButtonHolder}
>
<PlusCircle style={styles.addButton} width={70} height={70} stroke={"white"} strokeWidth={3}/>
</TouchableOpacity>
after:
<View style={styles.addButtonHolder}>
<TouchableOpacity onPress={()=> addCallback()}>
<PlusCircle style={styles.addButton} width={70} height={70} stroke={"white"} strokeWidth={3}/>
</TouchableOpacity>
</View>
StyleSheet:
const styles = StyleSheet.create({
addButtonHolder: {
position: 'absolute',
bottom: 70,
right: 10,
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
addButton: {
backgroundColor: '#b4cffa',
borderRadius: 35
}
})
This worked for me
import { TouchableOpacity } from 'react-native-gesture-handler'
and changed onPress to onPressIn
<TouchableOpacity onPressIn={() => console.log('clicked')}></TouchableOpacity>
My solution was to import TouchableHighlight from 'react-native'
It was originally imported from 'react-native-gesture-handler'
This props help to disable ScrollView to catch all touches and let child handles
keyboardShouldPersistTaps='always'
'always', the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.
'handled', the keyboard will not dismiss automatically when the tap was handled by children of the scroll view (or captured by an ancestor).
https://reactnative.dev/docs/scrollview#keyboardshouldpersisttaps
When the position is absolute, TouchableHighlight or TouchableOpacity goes beneath from the surface. You have to add a higher zIndex value to the Container.
He guy, I took a long time to find out why this happens.
I tested a lot of the solution here. Two things worked for:
The first answer here
The second one is to add elevation and zIndex on the wrapper container
<View style={{ zIndex:0 }>
...
<View style={{ position: 'absulote', zIndex:10 ,elevation: 10 }}>
<TouchableHighLight><Text>Click me</Text></TouchableHighlight>
</View>
<View> .... </View>
...
</View>
If I am right, the reason for that is even that the button is shown, Android treats differently the layers of the Press events and you need to set a low level for the rest of your components. Defining a lower level for your wrapper component, all its children without this attribute will automatically inherit from the parent.
My problem was quite different, a backgroundColor style property was set on the container of my button. The color didn't work. I missed to remove this useless property. Finally, this backgroundColor was making an invisible sheet above my button. Removing it make it clickable again.
I faced the problem only on Android.
Add a zIndex to the component where you added position absolute. This solved the issue for me.
position: 'absolute',
zIndex: 1,
`
I ran into a similar problem, what I did was, I enclosed the whole thing into a new View and instead of giving 'absolute' position to
the TouchableOpacity, I gave it to the parent View. That made the
Opacity again clickable somehow. Here is the code snippet of before
and after
My Code before
<TouchableOpacity
onPress={() => {
console.log("hah");
}}
style={{
height: 50, width: 50,
backgroundColor: 'rgb(90,135,235)',
borderRadius: 25, alignItems: 'center',
justifyContent: 'center', right: 0,position:'absolute'
}}>
<Image source={require('../assets/images/element-acorn-white.webp')}
style={{ height: 30, width: 30, resizeMode: 'contain' }} />
</TouchableOpacity>
After Wrapping into a View with 'absolute'
<View style={{
alignItems: 'flex-end', position: 'absolute',
bottom: Dimensions.get('screen').height / 5
}}>
<TouchableOpacity
onPress={() => {
console.log("hah");
}}
style={{
height: 50, width: 50,
backgroundColor: 'rgb(90,135,235)',
borderRadius: 25, alignItems: 'center',
justifyContent: 'center', right: 0,
}}>
<Image source={require('../assets/images/element-acorn-white.webp')}
style={{ height: 30, width: 30, resizeMode: 'contain' }} />
</TouchableOpacity>
</View>