I have created a Bottomsheet Component that mimicates the google maps BootmSheet. Th component has 3 different view states: CLOSED, SUMMARY, DETAILS. I have used Animated.View with position:absolute and an animated value which controls bottom style rule. SUMMARY and DETAILS have different animated values because I want to show the effect that one is opening and the other is opening. So my render and the constructor is something like that
constructor(props) {
super(props)
this.state = {
animatedPosition: new Animated.Value(-(DEVICE.height)),
animatedPositionFull: new Animated.Value(-(DEVICE.height)),
}
}
render() {
<>
<Animated.View style={{
position: 'absolute',
height: CONTENT_HEIGHT,
bottom: animatedPosition
}}>
{contentSummaryBottomsheet}
</Animated.View >
<Animated.View style={{
position: 'absolute',
height: FULL_CONTENT_HEIGHT,
bottom: animatedPositionFull
}}>
{contentDetailsBottomShet}
</Animated.View >
</>
}
When the state is NONE nothing is rendered. When the state is SUMMARY the above animated view is rendered with height about half of screen When the state is DETAILS I want the component to take the whole screen. In componentDidUpdate I check what animation to do
componentDidUpdate(prevProps) {
//9 scenarios
/*
A: closed-> closed | full -> full | summary -> summary
B: closed-> full | closed -> summary
C: half -> closed | full -> closed
D: half -> full | full -> half
*/
console.log("Did Update")
//if the state changed
if (prevProps.displayState != this.props.displayState) {
//if now the state is closed and before it was anything but closed, close it regardless of what that state was
if (prevProps.displayState !== BottomSheetStates.CLOSED && this.props.displayState === BottomSheetStates.CLOSED) {
//Scenario C:
this.animateCloseSummary()
this.animateCloseFull()
} else {
//and if it was closed before
if (prevProps.displayState === BottomSheetStates.CLOSED) {
//Scenario B:
//and now we go to summary show summary
if (this.props.displayState === BottomSheetStates.OPEN_SUMMARY) {
this.animateOpenSummary();
}
//and now we go to full show full
if (this.props.displayState === BottomSheetStates.OPEN_DETAILS) {
this.animateOpenFull();
}
}
//SCENARIO D:
else if (prevProps.displayState === BottomSheetStates.OPEN_DETAILS) {
//if it was open in full and we go to summary close full and show summary
this.animateCloseFull();
this.animateOpenSummary();
} else if (prevProps.displayState === BottomSheetStates.OPEN_SUMMARY) {
//if it was open in summary and we go to full close summary and show full
this.animateCloseSummary();
this.animateOpenFull();
}
}
} else {
}
}
animateOpenSummary = () => {
let { animationTime } = this.props
let { animatedPosition } = this.state
Animated.timing(animatedPosition, {
toValue: 10,
duration: animationTime,
}).start()
}
animateCloseSummary = () => {
let { animationTime } = this.props
let { animatedPosition } = this.state
Animated.timing(animatedPosition, {
toValue: -(DEVICE.height),
duration: animationTime,
}).start()
}
I tried to use flex:1 but with absolute position but it doesn't do what I wanted. So the only way I found to do what I want is to calculate the height of the window. In iOS using Dimensions API along with some checking if the device has notch gives me what I want . In Android I didn't found any consistent way to calculate the height. Dimensions API works different if the device has notch. So I used the react-native-device-info package to find if the device has notch. The way that the module finds if the device has notch is to have an array with device model names and check if the array contains the current model name. This way is not very accurate especially if you consider that every day a new device comes up with notch .
let DEVICE = Dimensions.get('window')
const STATUS_BAR_HEIGHT = StatusBar.currentHeight || 20
let isIPhoneX = (DEVICE.height == 812 || DEVICE.height == 896)
const STATUS_BAR_IOS = isIPhoneX ? 64 : 20
let iosHeight = Dimensions.get('screen').height - STATUS_BAR_IOS
const CONTENT_HEIGHT = DEVICE.height/2
const FULL_CONTENT_HEIGHT = Platform.OS === "android" ? DeviceInfo.hasNotch() ? DEVICE.height : DEVICE.height - STATUS_BAR_HEIGHT : iosHeight
My next approach was on the bootstrapping of the application to use a SafeAreaView with flex:1 and on the layout event to get the height and store is a global. This is a consistent way but it creates a dependency on the BottonmSheet Component that I would like to avoid.
<SafeAreaView style={{flex: 1 }} onLayout={(evn) => { setHeight(evn.nativeEvent.layout.height)
}}>
So I need to ask if there is way to get the window height