how to make modal to center in react native - react-native

How I make my box to center like justify content center but with position absolute ?
top: '50%' is too close to the bottom its not centered.
Modal.tsx
import React, { useEffect } from 'react';
import { StyleSheet, View, Pressable, StyleProp, ViewStyle } from 'react-native';
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
interface IModalCenter {
children: React.ReactNode;
onPress: () => void;
style?: StyleProp<ViewStyle>;
}
const ModalCenter = ({ children, onPress, style }: IModalCenter) => {
const x = useSharedValue(0);
useEffect(() => {
x.value = 1;
}, []);
const aView = useAnimatedStyle(() => {
const scale = withSpring(x.value);
return {
transform: [{ scale }]
}
});
return (
<GestureHandlerRootView style={s.container}>
<Animated.View style={{flexGrow: 1, backgroundColor: 'rgba(0,0,0,0.4)',}} />
<Animated.View style={[style, aView]}>
{ children }
</Animated.View>
</GestureHandlerRootView>
)
};
const s = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
flex: 1,
zIndex: 600,
margin: 'auto'
}
})
export default ModalCenter;
app.tsx
<ModalCenter style={{backgroundColor: '#fff', position: 'absolute', margin: 'auto', alignSelf: 'center', height: 200, width: 300, borderRadius: 8, elevation: 4}} onPress={handleToggleModalMessage}>
<Text>Hello</Text>
</ModalCenter>
how can i make it center ? ........................................................................................................................................................................................

You need to add 'justify-content' property to the container:
container: {
...StyleSheet.absoluteFillObject,
flex: 1,
zIndex: 600,
margin: 'auto',
justify-content: 'center' <---- add this
}

Related

How to animate increasing height of a View and then on some state change, reverse animation (decreasing height of View)?

I have a button when pressing that, want to increase height of view in an animated way. And then after pressing that button again, want to reverse the animation to get the initial height of view.
I have done animating increasing the height of view but when I give the new state to my component, reverse animation is not happening. How to solve this problem?
This is my screen:
import React, { useEffect, useRef, useState } from "react";
import {
Animated,
Easing,
Pressable,
StyleSheet,
Text,
View,
} from "react-native";
import AnimatedTile from "../components/AnimatedTile";
function Test(props) {
const [start, setStart] = useState(false);
return (
<View style={styles.container}>
<View style={styles.animationContainer}>
<Pressable
android_ripple={{ color: "red" }}
onPress={() => setStart(!start)}
>
<Text>animate</Text>
</Pressable>
<AnimatedTile startAnimation={start} /> // ---->My animated component
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
animationContainer: {
flexDirection: "row",
width: "60%",
height: "20%",
justifyContent: "space-between",
alignItems: "center",
},
});
export default Test;
And this is my animated component:
import React, { useEffect, useRef, useState } from "react";
import {
Animated,
Easing,
Pressable,
StyleSheet,
Text,
View,
} from "react-native";
function AnimatedTile({ startAnimation }) {
const height = new Animated.Value(0);
const isExpanded = useRef(false);
const maxHieght = height.interpolate({
inputRange: [0, 1],
outputRange: isExpanded.current ? [180, 10] : [10, 180],
});
const start = () => {
if (isExpanded.current) {
Animated.timing(height, {
toValue: 0,
duration: 500,
easing: Easing.linear,
useNativeDriver: false,
}).start();
} else {
Animated.timing(height, {
toValue: 1,
duration: 500,
easing: Easing.linear,
useNativeDriver: false,
}).start();
}
isExpanded.current = !isExpanded.current;
};
useEffect(() => {
start();
}, [startAnimation]);
return (
<Animated.View
style={{
width: 5,
height: 120,
maxHeight: maxHieght,
backgroundColor: "dodgerblue",
borderRadius: 5,
}}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
animationContainer: {
flexDirection: "row",
width: "60%",
height: "20%",
justifyContent: "space-between",
alignItems: "center",
},
});
export default AnimatedTile;

react native gesture handler how to make overlay

I want to make a bottom sheet, but I dont know why my overlay not working with a opacity background.
App.tsx
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import BottomSheet from './components/BottomSheet';
export default function App() {
return (
<GestureHandlerRootView style={s.container}>
<Text>Hello</Text>
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center', opacity: 0.8, backgroundColor: 'rgba(0,0,0,0.9)', zIndex: 10}}>
<BottomSheet />
</View>
</GestureHandlerRootView>
)
}
const s = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'red',
alignItems: 'center',
justifyContent: 'center'
}
});
BottomSheet.tsx
import { Dimensions, StyleSheet, Text, View } from 'react-native'
import React, { useEffect } from 'react'
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
const { height } = Dimensions.get('screen');
const START_HEIGHT = height / 3;
const BottomSheet = () => {
const translateY = useSharedValue(0);
const context = useSharedValue(0);
const panGesture = Gesture.Pan()
.onStart(() => {
context.value = translateY.value;
})
.onUpdate((e) => {
translateY.value = e.translationY + context.value;
translateY.value = Math.max(translateY.value, -height / 3);
})
.onEnd(() => {
if(translateY.value >= -height / 4) {
translateY.value = withTiming(0, { duration: 100 });
} else {
translateY.value = withSpring(-height / 3, { damping: 50 })
}
});
useEffect(() => {
translateY.value = -height / 3
}, []);
const rBottomSheet = useAnimatedStyle(() => {
return {
transform: [{ translateY: translateY.value }]
}
});
return (
<GestureDetector gesture={panGesture}>
<Animated.View style={[s.bottomSheet, rBottomSheet]}>
<View style={s.line} />
</Animated.View>
</GestureDetector>
)
}
export default BottomSheet
const s = StyleSheet.create({
bottomSheet: {
backgroundColor: '#fff',
height: height,
width: '100%',
position: 'absolute',
top: height,
borderRadius: 25
},
line: {
height: 4,
width: 70,
marginVertical: 15,
backgroundColor: 'grey',
borderRadius: 6,
alignSelf: 'center'
}
})
Image:
What I am doing wrong ? Can anyone help me please
..sa..................................................................................................................................................................................

Too Many renders. React Limits the number of renders to prevent infinite loop

am getting an error while running my react native app in expo , my error is Too many renders react limits the number of renders to prevent infinite loop, don't know where am wrong , please try to fix my error. if you have any question please free feel to ask ant time.
Home.js
This is the home.js file where in wrote my all code including css.
import React, { useEffect, useState } from 'react'
import { Text, View, FlatList, StyleSheet, ScrollView, Image } from 'react-native';
import { Avatar } from 'react-native-elements';
import { Searchbar, shadow, Modal, Provider, Portal } from 'react-native-paper';
import { AntDesign } from '#expo/vector-icons';
export default function Home() {
const [searchquery, setSearchquery] = React.useState();
const [visible, setVisible] = React.useState(false);
const showModal = setVisible(true);
const hideModal = setVisible(false);
const containerStyle = { backgroundColor: 'white', padding: 20 };
const [users, setUser] = useState([
{
id: 1,
name: "Ashish Nirvikar"
},
{
id: 2,
name: "Drew Macntyre"
},
{
id: 3,
name: "Jonh Cena"
},
{
id: 4,
name: "Rock Samoa"
},
{
id: 5,
name: "Boby Lashely"
},
])
return (
<View >
<Searchbar
placeholder="Search Contacts"
onChangeText={(query) => setSearchquery(query)}
value={searchquery}
style={{ marginTop: 30, marginHorizontal: 10 }}
/>
<ScrollView>
{
users.map((item, index) => {
return (
<View key={index}>
<Text style={styles.names}>{item.name}</Text>
</View>
)
})
}
</ScrollView>
<Provider>
<Portal>
<Modal visible={visible} onDismiss={hideModal} contentContainerStyle={containerStyle}>
<Text>Example Modal. Click outside this area to dismiss.</Text>
</Modal>
</Portal>
<AntDesign name="plus" size={34} color="black" style={styles.plus} onPress={showModal} />
</Provider>
</View>
);
}
const styles = StyleSheet.create({
customText: {
padding: 10,
marginTop: 20,
textAlign: 'center',
backgroundColor: 'lightgray',
fontWeight: 'bold',
fontSize: 20
},
plus: {
fontSize: 50,
position: 'absolute',
top: 680,
right: 40,
backgroundColor: 'pink',
borderRadius: 15,
borderWidth: 0.5,
padding: 5,
},
names: {
padding: 15,
fontSize: 25,
fontWeight: 'bold',
backgroundColor: 'lightgray',
marginTop: 10,
borderRadius: 20,
color: 'black'
}
});
The showModal and hideModal are functions, define it as a function.
const showModal = () => setVisible(true);
const hideModal = () => setVisible(false);

Issue with React Native animations

I'm facing a problem with centring the text after the animation finishes as you can see in the video here https://www.youtube.com/watch?v=hhBGUp9_GAY&feature=youtu.be. I want to get both titles perfectly centered horizontally on all devices no matter the screen width. I'm using the Animated API. Any suggestions?
Here is my approach
import React, { useEffect } from "react";
import { View, StyleSheet, Animated, Text, Dimensions, AsyncStorage } from "react-native";
export default function Welcome({ navigation }) {
const width = Dimensions.get('screen').width
let position1 = new Animated.ValueXY(0, 0);
let position2 = new Animated.ValueXY(0, 0);
useEffect(() => {
Animated.timing(position1, {
toValue: { x: width / 4.5, y: 0 },
duration: 900
}).start();
Animated.timing(position2, {
toValue: { x: -width / 3, y: 0 },
duration: 900
}).start();
}, []);
_retrieveData = async () => {
try {
const token = await AsyncStorage.getItem('tokehhn');
if (token !== null) {
// We have data!!
setTimeout(() => navigation.navigate('Home'), 2000)
} else {
setTimeout(() => navigation.navigate('Auth'), 2000)
}
} catch (error) {
// Error retrieving data
}
};
useEffect(() => {
_retrieveData()
}, [])
return (
<View style={styles.container}>
<Animated.View style={position1.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', fontWeight: 'bold', fontSize: 24, color: '#5790f9' }}>Welcome to Glue</Text>
</Animated.View>
<Animated.View style={position2.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', right: -220, fontWeight: 'bold', fontSize: 21, color: '#5790f9' }}>Where everything happens</Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
}
});
Thats how you do it:
let {width} = Dimensions.get('window')
export default function App() {
let animation = new Animated.Value(-width);
let translateX = animation.interpolate({inputRange:[-width,0],outputRange:[2*width,0]});
React.useEffect(()=>{
Animated.timing(animation,{toValue:0}).start();
},[])//eslint-ignore-line
return (
<View style={styles.container}>
<Animated.Text style={[styles.text,{transform:[{translateX:animation}]}]}>LOL</Animated.Text>
<Animated.Text style={[styles.text,{transform:[{translateX}]}]}>Longer LOLLLL</Animated.Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
text:{
textAlign:'center'
}
});
I have created snack as well
Make it a simple and clean interpolation.
The code looks always clean, and readable if we use Animated.Value in range of 0 - 1.
Full code:
import React, {useEffect} from 'react';
import {View, StyleSheet, Animated} from 'react-native';
const App = () => {
const animate = new Animated.Value(0);
const inputRange = [0, 1];
const translate1 = animate.interpolate({inputRange, outputRange: [-100, 0]});
const translate2 = animate.interpolate({inputRange, outputRange: [100, 0]});
useEffect(() => {
Animated.timing(animate, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<View style={styles.container}>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate1}]}]}>
First Text
</Animated.Text>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate2}]}]}>
Second Text
</Animated.Text>
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 25,
},
});
Using that animated value, implement any other animations if needed.
For example, If you need to scale the text while moving:
const scale = animate.interpolate({inputRange, outputRange: [1, 1.5]});

React native drawer not opening on swipe over map

I am using react-native-navigation to start a single screen app that contains a Google map using - react-native-maps and a left drawer:
Navigation:
Navigation.startSingleScreenApp({
screen: {
screen : 'map.MapScreen',
title : 'Map',
navigatorStyle: {
navBarHidden: true
}
},
drawer : {
left : {
screen : 'drawer.DrawerScreen',
passProps: {}
},
disableOpenGesture: true
},
animationType: 'slide-down',
passProps: {}
})
MapScreen:
export default class MapScreen extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
provider={PROVIDER_GOOGLE}>
</MapView>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
zIndex: -1
},
map: {
...StyleSheet.absoluteFillObject,
},
});
Unfortunately if I try to open the drawer, by swiping to the right, over the map, it won't open.
Does anyone have any idea on how to fix this?
You can use an overlay to add some invisible edge on the side of the map that does capture the gesture for opening the drawer.
looks something like this:
const Screen = {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
};
class Foo extends React.Component<Props, object> {
render() {
return (
<View style={styles.container}>
<MapView
style={styles.mapContainer}
/>
<View style={styles.mapDrawerOverlay} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
mapContainer: {
width: Screen.width,
height: Dimensions.get('window').height,
},
mapDrawerOverlay: {
position: 'absolute',
left: 0,
top: 0,
opacity: 0.0,
height: Dimensions.get('window').height,
width: 10,
},
});
This will use an overlay that is transparent and covers a small fraction of the map view. Beginning a drag-gesture in this area now can trigger the drawer.
Do not use ...StyleSheet.absoluteFillObject on your map styles.
Doing this will resolve your issue
const React = require("react-native");
const { StyleSheet, Dimensions } = React;
const { width, height } = Dimensions.get("window");
export default {
map: {
width: width,
height: height
}
};
You need to activate ToggleDrawer() when you hit the side of the map which is covered by a thin TouchableOpacity window. here is the example code in homeview. make sure to bring in props as a variable to your function.
import React from 'react';
import {View, Text, SafeAreaView, StyleSheet, Dimensions, TouchableOpacity} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
const HomeScreen = (props) => {
return(
<SafeAreaView style = {{flex: 1}}>
<View style = {styles.container}>
<MapView
style={styles.mapStyle}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
}
/>
</MapView>
<TouchableOpacity style = {styles.mapDrawerOverlay} onPress = {() => {
props.navigationProps.toggleDrawer();}
} />
</View>
</SafeAreaView>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'flex-end',
},
mapDrawerOverlay:{
position: 'absolute',
left: 0,
top: 0,
opacity: 0.0,
height: Dimensions.get('window').height,
width:10,
},
mapStyle: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
});