React Native - How to implement modal on a view? - javascript

As per title, I would like to have a modal opened when clicked on a view
app.js
<View>
<MediaBar onClick={this.toggleModal}> />
<Modal show={this.state.isOpen}
onClose={this.toggleModal}>
`Here's some content for the modal`
</Modal>
</View>
Mediabar.js: https://pastebin.com/6bW5gz99
modal.js: https://pastebin.com/3vQgLyTg

The Modal component has a visible property.
https://facebook.github.io/react-native/docs/modal.html
You just have to update its value using your parent component state.

I will do something like this
App.js
import React, { Component } from 'react';
import { View, Text, Modal, TouchableWithoutFeedback, Dimensions, TouchableOpacity } from 'react-native';
class App extends Component {
state = {
modalVisible: false
}
render() {
return(
<View style={this.styles.viewStyle}>
<Modal visible={this.state.modalVisible}
transparent={true}>
<View style={this.styles.viewStyle}>
<View style={this.styles.dialogStyle}>
</View>
</View>
</Modal>
<View style={this.styles.anotherView}>
<TouchableOpacity onPress={()=> this.setState({ modalVisible: true})}>
<Text>Hello</Text>
</TouchableOpacity>
</View>
</View>
);
}
styles = {
viewStyle: {
flex:1,
alignItems: 'center',
justifyContent: 'center'
},
dialogStyle: {
height: Dimensions.get('window').height*0.2,
width: Dimensions.get('window').width*0.9,
backgroundColor: '#d3d3d3',
elevation: 10,
borderRadius: 5
},
anotherView:{
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
}
}
}
export default App;
To Achieve the modal like below:

Related

How to add information pop-up for TextInput in React Native?

I want to achieve something like this in React Native:
I have a TextInput component and I want to put an icon to the right side. The user can click it, then I can display some text in a modal or in a another component.
Is this possible in react native?
return(
<View style={styles.container}>
<TextInput
placeholder="Állat neve"
value={AllatNev}
style={styles.textBox}
onChangeText={(text) => setAllatNev(text)}
/>
</View>
);
}
)
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: color_theme_light.bodyBackground
justifyContent: 'center',
alignItems:'center'
},
textBox:{
borderWidth:2,
borderColor: color_theme_light.textBoxBorder,
margin:15,
borderRadius:10,
padding: 10,
fontFamily:'Quicksand-Medium'
},
});
Yes -- you can position your info button over the TextInput using absolute positioning and a zIndex, for example:
import * as React from 'react';
import { Text, View, StyleSheet, TextInput, TouchableOpacity } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<View style={styles.textBoxParent}>
<TextInput style={styles.textBox} placeholder="Állat neve"/>
<TouchableOpacity style={styles.textBoxButton} onPress={() => {
//launch your modal
}}>
<Text>i</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
textBoxParent: {
justifyContent: 'center'
},
textBox:{
borderWidth:2,
borderColor: 'gray',
margin:15,
borderRadius:10,
padding: 10,
},
textBoxButton: {
position: 'absolute',
right: 20,
zIndex: 100,
width: 20,
height: 20,
borderWidth: 1,
borderRadius: 10,
justifyContent: 'center',
alignItems: 'center'
}
});
Working example: https://snack.expo.dev/OFMTc8GHE
Heres a full example of what you want (https://snack.expo.dev/bjzBFuE4W). And below I explain the code.
Fist I made a Modal from react native that takes in modalVisible, setModalVisible, and appears when modalVisible is true.
import * as React from 'react';
import { Text, View, StyleSheet,TextInput ,TouchableOpacity,Modal} from 'react-native';
import { AntDesign } from '#expo/vector-icons';
import { MaterialIcons } from '#expo/vector-icons';
const ModalInfo = ({modalVisible, setModalVisible})=>{
return (
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}
>
<View style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}>
<View
style={{
width:200,height:200,backgroundColor:"gray",borderWidth:2,
justifyContent:"center",alignItems:"center"
}}
>
<TouchableOpacity onPress={()=>{setModalVisible(false)}}>
<MaterialIcons name="cancel" size={24} color="black" />
</TouchableOpacity>
</View>
</View>
</Modal>
)
}
Next I made a View to wrap around the textInput so I can also add an svg of the info icon. And then set the outside view to have flexDirection:"row", so everything would be ordered the way you wan't.
const TextInputWithModal = ()=>{
const [modalVisible, setModalVisible] = React.useState(false);
const [AllatNev,setAllatNev]= React.useState("");
return (
<View style={styles.textInputContainer}>
<TextInput
placeholder="Állat neve"
value={AllatNev}
style={styles.textBox}
onChangeText={(text) => setAllatNev(text)}
/>
<TouchableOpacity onPress={()=>{setModalVisible(true)}}>
<AntDesign name="infocirlceo" size={24} color="black" />
</TouchableOpacity>
<ModalInfo modalVisible={modalVisible} setModalVisible={setModalVisible}/>
</View>
)
}
export default function App() {
return (
<View style={styles.container}>
<TextInputWithModal/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems:"center",
},
textInputContainer:{
borderRadius:10,
padding: 10,
flexDirection:"row",
margin:15,
borderWidth:2,
},
textBox:{
fontFamily:'Quicksand-Medium',
marginRight:20,
},
});

TextBox above Keyboard

I am new to React Native and I have encountered a problem: my TextBox doesn't stay above the keyboard while I am writing in it.
These are the visuals from the application
Screenshot
And this is my Signup.js:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
import Form from '../components/Form';
import Logo2 from'../components/Logo2';
import {Actions} from 'react-native-router-flux';
export default class Signup extends Component {
goBack() {
Actions.pop()
}
render() {
return(
<View style={styles.container}>
<Logo2/>
<Form type="Registrar"/>
<View style={styles.signupTextCont}>
<Text style={styles.signupText}>Ja tens uma conta? </Text>
<TouchableOpacity onPress={this.goBack}><Text style={styles.signupButton}>Entra</Text></TouchableOpacity>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#232122'
},
signupTextCont: {
flexGrow: 1,
justifyContent: 'center',
alignItems: 'flex-end',
paddingVertical: 16,
flexDirection: 'row'
},
signupText: {
color: 'rgba(255,255,255,0.7)',
fontSize:16
},
signupButton: {
color: '#FFA200',
fontSize:16,
fontWeight: '500'
}
});
I don't know what I need to code.
Could you please help me?
To replicate my comment that resolved your problem; you can use the component KeyboardAvoidingView from the package react-native. It levels up the area it contains above your virtual keyboard.
Wrap your code like this:
<KeyboardAvoidingView behavior={'position'} enabled>
<View style={styles.signupTextCont}>
<Text style={styles.signupText}>Ja tens uma conta? </Text>
<TouchableOpacity onPress={this.goBack}>
<Text style={styles.signupButton}>Entra</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
Here's some more info about how it works: https://facebook.github.io/react-native/docs/keyboardavoidingview.
You can wrap your code with KeyboardAvoidingView rather than a normal View so that it can actually lift up any items inside it above the keyboard. You can use <KeyboardAvoidingView behavior="padding">
Hope it helps.

Modal won't show the image in react native

I have written a program in which I am showing a list of images in ScrollView when user clicks an image a modal is popped up and image would be shown in large size. But this is not working.
Modal is opening fine but it is not showing the image..
However I have checked the value of image in render method of Modal i.e. ImageViewer, value I am getting is a class object.
App:
import React, {Component} from 'react';
import {View,Modal, ScrollView, Image, TouchableOpacity, StyleSheet} from 'react-native';
import pic1 from './images/sl13.png';
import pic2 from './images/sl14.png';
import pic3 from './images/sl15.png';
import pic4 from './images/sl16.png';
import pic5 from './images/sl17.png';
class App extends Component {
Images = [pic1, pic2, pic3, pic4, pic5];
state = {
modalVisible: false,
}
image = null
close(){
this.setState({modalVisible: false});
}
constructor(props){
super(props);
this.close = this.close.bind(this);
}
showImage(path){
this.image = path;
this.setState({modalVisible: true});
}
render() {
return (
<View style={{alignItems:'center', justifyContent: 'center'}}>
<ScrollView>
{this.Images.map((item)=>{
return (
<TouchableOpacity key={item} onPress={(item)=>this.showImage(item)}>
<View style={{borderColor: 'red', borderWidth: 1, marginBottom: 10}}>
<Image style={{width: 200, height: 200}} source={item} />
</View>
</TouchableOpacity>
)
})}
</ScrollView>
<ImageViewer closeModal={()=>this.close()} modalVisible={this.state.modalVisible} image={this.image}/>
</View>
);
}
}
Modal:
class ImageViewer extends Component {
render() {
console.log(this.props.image) //Checking Value here
return (
<Modal
style={{top: '50%', left: '50%', transform: 'translate(-50%, -50%) !important'}}
animationType='fade'
transparent={true}
onRequestClose={()=>this.props.closeModal()}
visible={this.props.modalVisible}
>
<View style={{flex:1 ,alignItems: 'center', justifyContent: 'center', backgroundColor:'#00000069'}}>
<View style={{padding:20 , backgroundColor:'#fff', borderRadius: 10}}>
<Image style={{width: 400, height: 600}} source={this.props.image} />
</View>
</View>
</Modal>
)
}
}
export default App;
both are in same file..
Screenshots :
Normal : https://imgur.com/deai02Y.jpg
Clicked on Image: https://imgur.com/POuZlPU.jpg
On Touchableopacity onPress you are actually passing the onPress $event to showImage function instead of item. so correct way of doing it is
<View style={{alignItems:'center', justifyContent: 'center'}}>
<ScrollView>
{this.Images.map((item)=>{
return (
<TouchableOpacity key={item} onPress={()=>this.showImage(item)}>
<View style={{borderColor: 'red', borderWidth: 1, marginBottom: 10}}>
<Image style={{width: 200, height: 200}} source={item} />
</View>
</TouchableOpacity>
)
})}
</ScrollView>
<ImageViewer closeModal={()=>this.close()} modalVisible={this.state.modalVisible} image={this.image}/>
</View>
after app called write
constructor(props) {
super(props);
this.state = {
image : ''
};
}
and in showImage path
showImage(path){
this.setstate({image : path});
this.setState({modalVisible: true});
}
and in Imageviewer
<ImageViewer closeModal={()=>this.close()} modalVisible={this.state.modalVisible} image={this.state.image}/>

How to show header title in React Native using React Navigation

I am newbie using React Native, I have a problem to show/display header title using React Navigation, I try to look for but fail all.
This is my complete script:
Loginscreen.js
class LoginScreen extends Component {
static navigationOptions = {
title: 'Login',
};
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Login Screen</Text>
<Button title="Login To" onPress={() => this.props.navigation.navigate('Tabs')} />
<Button title="Go To Register" onPress={() => this.props.navigation.navigate('Register')} />
</View>
);
}
}
RegisterScreen.js
class RegisterScreen extends Component {
static navigationOptions = {
title: 'Register',
};
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Register Screen</Text>
<Button title="Go to Login" onPress={() => this.props.navigation.navigate('Login')} />
</View>
);
}
}
HomeScreen.js
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button title="Go to Login" onPress={() => this.props.navigation.navigate('Login')} />
</View>
);
}
}
ProfileScreen.js
class ProfileScreen extends Component {
static navigationOptions = {
title: 'Profile',
};
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button title="Go to Login" onPress={() => this.props.navigation.navigate('Login')} />
</View>
);
}
}
App.js
and I get Result like this :
Please anyone help me to show/display header title (circle red), header title in component LoginScreen and Register are fine, but in component HomeScreen and ProfileScreen are lost. Please help me to show/display it.
Thanks.
Define headerMode: 'screen', inside your stacknavigator.
i.e. your rootStack
const rootStack = createStackNavigator(
{
},{
headerMode: 'screen'
})
You must reload the application if you have not, because navigationOptions are static therefore they load only once when app launches and they don't update.

How do I overlay ActivityIndicator in react-native?

I have a View with few form elements and a button (TouchableHighlight). On clicking the button, an Activity Indicator should be shown as an overlay to the existing view. The Activity Indicator should be centered within the page and the existing view should be slightly blurred to indicate overlay. I tried different options but could not get it to work.
render() {
const { username, password } = this.state;
return (
<View style={styles.container}>
<View style={styles.group}>
<Text style={styles.text}>Username:</Text>
<TextInput
style={styles.input}
onChangeText={this.handleUserNameChange.bind(this)}
value={username}
underlineColorAndroid="transparent"
/>
</View>
<View style={styles.group}>
<Text style={styles.text}>Password:</Text>
<TextInput
style={styles.input}
secureTextEntry={true}
onChangeText={this.handlePasswordChange.bind(this)}
value={password}
underlineColorAndroid="transparent"
/>
</View>
<TouchableHighlight
style={styles.button}
onPress={this.handleLogin.bind(this)}>
<Text style={styles.buttonText}>Logon</Text>
</TouchableHighlight>
</View>
);
}
Existing styles:
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: '#F5FCFF',
marginTop: 60
},
group: {
alignItems: 'flex-start',
padding: 10
},
input: {
width: 150,
padding: 2,
paddingLeft: 5,
borderColor: 'gray',
borderWidth: 1
},
text: {
padding: 0
},
button: {
width: 150,
backgroundColor: '#2299F2',
padding: 15,
marginTop: 20,
borderRadius: 5
},
buttonText: {
textAlign: 'center',
color: '#fff',
fontSize: 24
},
});
I need to an ActivityIndicator to the above View, overlay the view, and center the ActivityIndicator.
For this to work, you'd need to absolute position it, and render it after the elements that should be underneath the overlay:
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
}
Then simply compose it into the render method conditionally, based on a loading state. I am going to assume this.handleLogin sets some sort of loading state already.
Make sure it's rendered last so it takes precedence.
...
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator size='large' />
</View>
}
Here is a complete example using create react native app.
import React from 'react';
import {StyleSheet, ActivityIndicator, View} from "react-native";
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {}
render() {
return (
<View
style={{flex: 1}}
>
//Add other content here
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator/>
</View>
}
</View>
);
}
}
showLoading() {
this.setState({loading: true})
}
hideLoading() {
this.setState({loading: false})
}
const styles = StyleSheet.create({
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
opacity: 0.5,
backgroundColor: 'black',
justifyContent: 'center',
alignItems: 'center'
}
})
You can use StyleSheet.absoluteFill to shorten code.
Add this to your render:
<View style={styles.container}>
//... other code here
{this.state.loading && <View
style={{
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
}}>
<ActivityIndicator />
</View>}
</View>
Improvement:
You can also create a Loading component:
Loading.js
import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';
export const Loading = ({theme = 'white', size = 'large'}) => {
const color = theme === 'white' ? '#00bdcd' : '#fff';
return (
<View
style={{
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
}}>
<ActivityIndicator size={size} color={color} />
</View>
);
};
Then use it anywhere you want
<View style={styles.container}>
//... other code here
// remember to import Loading component
{this.state.loading && <Loading />}
</View>
You can build a nice overlay using the activity indicator component by also leveraging the modal capabilities like Sanaur suggests.
For example you can use the below functional component. You can control it's visibility through the show prop that you can tie to a state in your screen.
An example that you can adapt to your needs.
const ModalActivityIndicator = props => {
const {
show = false,
color = "black",
backgroundColor = "white",
dimLights = 0.6,
loadingMessage = "Doing stuff ..."
} = props;
return (
<Modal transparent={true} animationType="none" visible={show}>
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: `rgba(0,0,0,${dimLights})`
}}
>
<View
style={{
padding: 13,
backgroundColor: `${backgroundColor}`,
borderRadius: 13
}}
>
<ActivityIndicator animating={show} color={color} size="large" />
<Text style={{ color: `${color}` }}>{loadingMessage}</Text>
</View>
</View>
</Modal>
);
};
and in your screen, in the render's return, just add it there as a child (please ignore the rest of the code, I put it there for context).
return (
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss();
}}
>
<View style={{ padding: 13, flex: 1}}>
<ModalActivityIndicator show={screenIsWaiting} />
<View
style={{
where screenIsWaiting is just a state, for example
const [screenIsWaiting, setScreenIsWaiting] = useState(false);
To test it you can add a button somewhere,
<Button
title="TestButton"
onPress={async () => {
setScreenIsWaiting(true);
await sleep(5000);
setScreenIsWaiting(false);
...
}}
/>
where sleep is a function defined as
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
I found the sleep() idea on stackoverflow on another post.
You can of course also define the
<ModalActivityIndicator show={screenIsWaiting} ... />
only once in your App's root component and trigger it's display and props via a global state container like redux.
There is a library available for this react-native-loading-spinner-overlay.
You can simply install it using
npm install react-native-loading-spinner-overlay --save
and can import into your project using
import Spinner from 'react-native-loading-spinner-overlay';
Here is how to use it
<Spinner
//visibility of Overlay Loading Spinner
visible={this.state.loading}
//Text with the Spinner
textContent={'Loading...'}
//Text style of the Spinner Text
textStyle={styles.spinnerTextStyle}
/>
STEP 1:
Create the component for the spinner:
export const OverlaySpinner = () => {
return (
<View style={styles.spinnerView}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
};
STEP 2:
Create the style for the spinner view (using zIndex is very important to make sure the view is over everything on the screen):
spinnerView: {
position: "absolute",
zIndex: 1,
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#F5FCFF88",
},
STEP 3:
Make the initial state for the spinning component:
const [showSpinner, setshowSpinner] = useState(true);
STEP 4:
Use the component and don't forget to import it (don't forget to dismiss the keyboard on submit)
{showSpinner && <OverlaySpinner />}
I suppose you should use Modal to overlay activity indicator. Following is an example:
<Modal
transparent={true}
animationType={'none'}
visible={loading}
onRequestClose={() => {console.log('close modal')}}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator
animating={loading} />
</View>
</View>
</Modal>
Add in view of loading
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
set in View of Activity Indicator
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
Here my code for a functional component for anyone looking to achieve this with nativebase as design system. useColorScheme is another hook I use for detecting dark mode.
import { Flex, Spinner } from "native-base"
import React from "react"
import useColorScheme from "../../hooks/useColorScheme"
export default function Loading() {
const colorScheme = useColorScheme()
return (
<Flex
position="absolute"
alignItems="center"
justifyContent="center"
top={0}
left={0}
right={0}
bottom={0}
backgroundColor={colorScheme === "dark" ? "coolBlack.500" : "white"}
>
<Spinner size="lg" color={colorScheme === "dark" ? "white" : "coolBlack.500"} />
</Flex>
)
}

Categories

Resources