how do i get react native to play multiple local files - javascript

i want to play multiple videos and i have this set up but the videos wont show i don't know if i have to add the path or am i just adding everything wrong from const i basically know how to play one video buy adding it to the souce but i want to play more but i don't know how to import them from a local file?
any suggestions would help thanks in advance
import React from 'react';
import { StyleSheet, Text, View, Dimensions,ImageBackground,ScrollView } from 'react-native';
import { Video } from 'expo';
import { MaterialIcons, Octicons } from '#expo/vector-icons';
const VIDEOS = [ // replace these links with links to your videos
'./spiderman.mp4',
'C:\Windows\System32\avengerproject2\spiderman.mp4',
'C:\Windows\System32\avengerproject2\spiderman.mp4'
]
export default class App extends React.Component {
state = {
currentVideo: 0,
mute: false,
shouldPlay: true,
}
handlePlayAndPause = () => {
this.setState(prevState => ({
shouldPlay: !prevState.shouldPlay
}));
}
handleVolume = () => {
this.setState(prevState => ({
mute: !prevState.mute,
}));
}
forwardButton = () => {
if (this.state.currentVideo != VIDEOS.length-1) {
this.setState({currentVideo: this.state.currentVideo + 1});
} else {
this.setState({currentVideo: 0});
}
}
backButton = () => {
if (this.state.currentVideo != 0) {
this.setState({currentVideo: this.state.currentVideo - 1});
} else {
this.setState({currentVideo: VIDEOS.length-1});
}
}
render() {
const { width } = Dimensions.get('window');
return (
<View style={styles.container}>
<ImageBackground
source={{uri:'https://wallpapercave.com/wp/aqm17QD.jpg'}}
style={{width: '100%', height: '100%'}}>
<View >
<Text style={{ textAlign: 'center', fontSize: 18,
fontWeight: 'bold' }}>Welcome spiderman</Text>
<Video source =
{{uri: VIDEOS[this.state.currentVideo]}}
shouldPlay={this.state.shouldPlay}
resizeMode="cover"
style={{ width, height: 300 }}
isMuted={this.state.mute}
/>
<View style={styles.controlBar}>
<MaterialIcons
name={this.state.mute ? "volume-mute" :
"volume-up"}
size={45}
color="white"
onPress={this.handleVolume}
/>
<MaterialIcons
name={this.state.shouldPlay ? "pause" :
"play-arrow"}
size={45}
color="white"
onPress={this.handlePlayAndPause}
/>
</View>
</View>
<View style={{flex: .25, flexDirection: 'row',
alignItems: 'center'}}>
<MaterialIcons
name={"navigate-before"}
size={150}
color="white"
onPress={this.backButton}
/>
<Text>Next Video</Text>
<MaterialIcons
name={"navigate-next"}
size={150}
color="white"
onPress={this.forwardButton}
/>
</View>
</ImageBackground>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#B32E2E',
alignItems: 'center',
justifyContent: 'center',
},
controlBar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 45,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
});

Your problem looks like you are unable to access to your video files. (Not sure about rest of your code also)
If you want to access any type of asset in your app, you have 2 choices.
1) Stream video from a server.
Which would allow you to just add a link of the video like you do with your local mp4 files. (https://videostoragesite.com/spiderman.mp4)
2) Add assets to your bundle.
Which you can do with Expo's asset manager, or eject your project to a vanilla react native project and bundle the mp4 files into your app using Xcode or Android Studio.

Related

React Native Flatlist element onPress not fired until List rendering is complete

I have a FlatList that receives (immutable) data of max. 50 elements and it renders in each list item Svg using react-native-svg.
Parts of the graphics are wrapped with a Pressable component for selecting the element.
Now the problem is, that I can't select any of the elements, until the FlatList went through all 50 items.
What I don't get is, that the offscreen items aren't even rendered, it's just the containers. Once it's all rendered, I can click the elements, the ripple effect shows and the event is fired.
Specs:
Expo # 46.0.0
React Native # 0.69.6
React # 18.0.0
Running with android via expo start --no-dev --minify then open in Expo Go
Reproduction:
import React, { useEffect, useState } from 'react'
import { FlatList } from 'react-native'
import { Foo } from '/path/to/Foo'
import { Bar } from '/path/to/Bar'
export const Overview = props => {
const [data, setData] = useState(null)
// 1. fetching data
useEffect(() => {
// load data from api
const loaded = [{ id: 0, type: 'foo' }, { id: 1, type: 'bar' }] // make a list of ~50 here
setData(loaded)
}, [])
if (!data?.length) {
return null
}
// 2. render list item
const onPressed = () => console.debug('pressed')
const renderListItem = ({ index, item }) => {
if (item.type === 'foo') {
return (<Foo key={`foo-${index}`} onPressed={onPressed} />)
}
if (item.type === 'bar') {
return (<Foo key={`bar-${index}`} onPressed={onPressed} />)
}
return null
}
// at this point data exists but will not be changed anymore
// so theoretically there should be no re-render
return (
<FlatList
data={data}
renderItem={renderListItem}
inverted={true}
decelerationRate="fast"
disableIntervalMomentum={true}
removeClippedSubviews={true}
persistentScrollbar={true}
keyExtractor={flatListKeyExtractor}
initialNumToRender={10}
maxToRenderPerBatch={10}
updateCellsBatchingPeriod={100}
getItemLayout={flatListGetItemLayout}
/>
)
}
}
// optimized functions
const flatListKeyExtractor = (item) => item.id
const flatListGetItemLayout = (data, index) => {
const entry = data[index]
const length = entry && ['foo', 'bar'].includes(entry.type)
? 110
: 59
return { length, offset: length * index, index }
}
Svg component, only Foo is shown, since Bar is structurally similar and the issue affects both:
import React from 'react'
import Svg, { G, Circle } from 'react-native-svg'
const radius = 25
const size = radius * 2
// this is a very simplified example,
// rendering a pressable circle
const FooSvg = props => {
return (
<Pressable
android_ripple={rippleConfig}
pressRetentionOffset={0}
hitSlop={0}
onPress={props.onPress}
>
<Svg
style={props.style}
width={size}
height={size}
viewBox={`0 0 ${radius * 2} ${radius * 2}`}
>
<G>
<Circle
cx='50%'
cy='50%'
stroke='black'
strokeWidth='2'
r={radius}
fill='red'
/>
</G>
</Svg>
</Pressable>
)
}
const rippleConfig = {
radius: 50,
borderless: true,
color: '#00ff00'
}
// pure component
export const Foo = React.memo(FooSvg)
The rendering performance itself is quite good, however I can't understand, why I need to wait up to two seconds, until I can press the circles, allthough they have already been rendered.
Any help is greatly appreciated.
Edit
When scrolling the list very fast, I get:
VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. {"contentLength": 4740, "dt": 4156, "prevDt": 5142}
However, the Components are already memoized (PureComponent) and not very complex. There must be another issue.
Hardware
I cross tested with an iPad and there is none if the issues described. It seems to only occur on Android.
Please ignore grammatical mistakes.
This is the issue with FlatList. Flat list is not good for rendering a larger list at one like contact list. Flatlist is only good for getting data from API in church's like Facebook do. get 10 element from API and. then in the next call get 10 more.
To render. a larger number of items like contact list (more than 1000) or something like this please use https://bolan9999.github.io/react-native-largelist/#/en/
import React, {useRef, useState} from 'react';
import {
Image,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import {LargeList} from 'react-native-largelist-v3';
import Modal from 'react-native-modal';
import {widthPercentageToDP as wp} from 'react-native-responsive-screen';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import fonts from '../constants/fonts';
import {moderateScale} from '../constants/scaling';
import colors from '../constants/theme';
import countries from '../Data/larger_countries.json';
const CountrySelectionModal = ({visible, setDefaultCountry, setVisible}) => {
const pressable = useRef(true);
const [country_data, setCountryData] = useState(countries);
const [search_text, setSearchText] = useState('');
const onScrollStart = () => {
if (pressable.current) {
pressable.current = false;
}
};
const onScrollEnd = () => {
if (!pressable.current) {
setTimeout(() => {
pressable.current = true;
}, 100);
}
};
const _renderHeader = () => {
return (
<View style={styles.headermainView}>
<View style={styles.headerTextBg}>
<Text style={styles.headerTitle}>Select your country</Text>
</View>
<View style={styles.headerInputBg}>
<TouchableOpacity
onPress={() => searchcountry(search_text)}
style={styles.headericonBg}>
<FontAwesome
name="search"
size={moderateScale(20)}
color={colors.textColor}
/>
</TouchableOpacity>
<TextInput
placeholder="Select country by name"
value={search_text}
placeholderTextColor={colors.textColor}
style={styles.headerTextInput}
onChangeText={text => searchcountry(text)}
/>
</View>
</View>
);
};
const _renderEmpty = () => {
return (
<View
style={{
height: moderateScale(50),
backgroundColor: colors.white,
flex: 1,
justifyContent: 'center',
}}>
<Text style={styles.notFoundText}>No Result Found</Text>
</View>
);
};
const _renderItem = ({section: section, row: row}) => {
const country = country_data[section].items[row];
return (
<TouchableOpacity
activeOpacity={0.95}
onPress={() => {
setDefaultCountry(country),
setSearchText(''),
setCountryData(countries),
setVisible(false);
}}
style={styles.renderItemMainView}>
<View style={styles.FlagNameView}>
<Image
source={{
uri: `https://zoobiapps.com/country_flags/${country.code.toLowerCase()}.png`,
}}
style={styles.imgView}
/>
<Text numberOfLines={1} ellipsizeMode="tail" style={styles.text}>
{country.name}
</Text>
</View>
<Text style={{...styles.text, marginRight: wp(5), textAlign: 'right'}}>
(+{country.callingCode})
</Text>
</TouchableOpacity>
);
};
const searchcountry = text => {
setSearchText(text);
const items = countries[0].items.filter(row => {
const result = `${row.code}${row.name.toUpperCase()}`;
const txt = text.toUpperCase();
return result.indexOf(txt) > -1;
});
setCountryData([{header: 'countries', items: items}]);
};
return (
<Modal
style={styles.modalStyle}
animationIn={'slideInUp'}
animationOut={'slideOutDown'}
animationInTiming={1000}
backdropOpacity={0.3}
animationOutTiming={700}
hideModalContentWhileAnimating={true}
backdropTransitionInTiming={500}
backdropTransitionOutTiming={700}
useNativeDriver={true}
isVisible={visible}
onBackdropPress={() => {
setVisible(false);
}}
onBackButtonPress={() => {
setVisible(false);
}}>
<LargeList
showsHorizontalScrollIndicator={false}
style={{flex: 1, padding: moderateScale(10)}}
onMomentumScrollBegin={onScrollStart}
onMomentumScrollEnd={onScrollEnd}
contentStyle={{backgroundColor: '#fff'}}
showsVerticalScrollIndicator={false}
heightForIndexPath={() => moderateScale(49)}
renderIndexPath={_renderItem}
data={country_data}
bounces={false}
renderEmpty={_renderEmpty}
renderHeader={_renderHeader}
headerStickyEnabled={true}
initialContentOffset={{x: 0, y: 600}}
/>
</Modal>
);
};
export default CountrySelectionModal;
const styles = StyleSheet.create({
modalStyle: {
margin: moderateScale(15),
borderRadius: moderateScale(10),
overflow: 'hidden',
backgroundColor: '#fff',
marginVertical: moderateScale(60),
justifyContent: 'center',
},
headermainView: {
height: moderateScale(105),
backgroundColor: '#fff',
},
headerTextBg: {
height: moderateScale(50),
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
headerTitle: {
textAlign: 'center',
fontFamily: fonts.Bold,
fontSize: moderateScale(16),
color: colors.textColor,
textAlignVertical: 'center',
},
headerInputBg: {
height: moderateScale(40),
borderRadius: moderateScale(30),
overflow: 'hidden',
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: moderateScale(10),
backgroundColor: colors.inputbgColor,
flexDirection: 'row',
},
headericonBg: {
backgroundColor: colors.inputbgColor,
alignItems: 'center',
justifyContent: 'center',
width: moderateScale(40),
height: moderateScale(40),
},
headerTextInput: {
backgroundColor: colors.inputbgColor,
height: moderateScale(30),
flex: 1,
paddingTop: 0,
includeFontPadding: false,
fontFamily: fonts.Medium,
color: colors.textColor,
paddingBottom: 0,
paddingHorizontal: 0,
},
notFoundText: {
fontFamily: fonts.Medium,
textAlign: 'center',
fontSize: moderateScale(14),
textAlignVertical: 'center',
color: colors.textColor,
},
renderItemMainView: {
backgroundColor: colors.white,
flexDirection: 'row',
alignSelf: 'center',
height: moderateScale(43),
alignItems: 'center',
justifyContent: 'space-between',
width: wp(100) - moderateScale(30),
},
FlagNameView: {
flexDirection: 'row',
justifyContent: 'center',
paddingLeft: moderateScale(12),
alignItems: 'center',
},
imgView: {
height: moderateScale(30),
width: moderateScale(30),
marginRight: moderateScale(10),
borderRadius: moderateScale(30),
},
text: {
fontSize: moderateScale(13),
color: colors.textColor,
marginLeft: 1,
fontFamily: fonts.Medium,
},
});

React Native - add a masked circle overlay over image

How do I go about adding an opaque circular overlay over an image in React Native? Similar to the instagram image picker:
as trivial a task this may seem, I've had a world of trouble replicating this. Any suggestions?
As someone mentioned in the comments, the way to achieve this is with React Native Masked View.
Install it in your project by running:
npm install -S #react-native-community/masked-view
or
yarn add #react-native-community/masked-view
Then you can use it as follows. I've adapted the example from their README for you here:
import MaskedView from '#react-native-community/masked-view';
import React from 'react';
import { View } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View
style={{
flex: 1,
backgroundColor: '#000000', // "Edge" background
maxHeight: 400,
}}
>
<MaskedView
style={{ flex: 1 }}
maskElement={
<View
style={{
// Transparent background mask
backgroundColor: '#00000077', // The '77' here sets the alpha
flex: 1,
}}
>
<View
style={{
// Solid background as the aperture of the lens-eye.
backgroundColor: '#ff00ff',
// If you have a set height or width, set this to half
borderRadius: 200,
flex: 1,
}}
/>
</View>
}
>
{/* Shows behind the mask, you can put anything here, such as an image */}
<View style={{ flex: 1, height: '100%', backgroundColor: '#324376' }} />
<View style={{ flex: 1, height: '100%', backgroundColor: '#F5DD90' }} />
<View style={{ flex: 1, height: '100%', backgroundColor: '#F76C5E' }} />
<View style={{ flex: 1, height: '100%', backgroundColor: '#2E6D3E' }} />
</MaskedView>
</View>
);
}
}
import React from 'react';
import {
SafeAreaView,
StyleSheet,
View,
Image,
Text
} from 'react-native';
const Test = () => {
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<Image
source={{
uri: 'https://raw.githubusercontent.com/AboutReact/sampleresource/master/old_logo.png'
}}
//borderRadius will help to make Round Shape
style={{
width: 200,
height: 200,
borderRadius: 200 / 2
}}
/>
<Text style={styles.textHeadingStyle}>
About React
</Text>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#e0dcdc',
},
textHeadingStyle: {
marginTop: 30,
fontSize: 40,
color: '#0250a3',
fontWeight: 'bold',
},
});
export default Test;
import React, { Component } from 'react';
import {
View,
StyleSheet,
Text,
ScrollView,
TouchableOpacity,
} from 'react-native';
import styles from './styles';
import { Circle, CustomHeader, CustomImage, CTNexaBold } from '../../components';
import translate from '../../translations/translate';
import { images, icons } from '../../assets'
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';
import utils from '../../utils';
import { Colors } from '../../common';
import ImagePicker from 'react-native-image-crop-picker';
class UploadProfilePicture extends Component {
constructor(props) {
super(props);
this.state = {
profileImage: '',
isProfileImage: false,
};
}
componentDidMount() {
};
changeProfilePhoto() {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true
}).then(image => {
this.setState({
profileImage: image.path,
isProfileImage: true,
})
});
}
render() {
const { profileImage, isProfileImage } = this.state
return (
<View style={styles.container}>
{utils.statusBar('dark-content', Colors.white)}
<CustomHeader
title={<CTNexaBold customStyle={styles.customStyle} >{translate("Upload Profile Picture")}</CTNexaBold>}
{...this.props}
/>
<View style={{ flex: 0.8, alignItems: 'center', justifyContent: 'center', marginBottom: 200 }} >
<View>
<Circle
width={wp('44%')}
height={wp('44%')}
borderRadius={wp('44%')}
borderColor={'#A28A3D'}
marginVertical={40}
marginHorizontal={70}
>
<CustomImage
style={styles.userAvatar}
// source={images.iconProfile}
source={ isProfileImage ? { uri: profileImage } : images.iconProfile }
/>
</Circle>
</View>
<View style={{ marginHorizontal: wp('10%') }} >
<TouchableOpacity onPress={()=>this.changeProfilePhoto()} >
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }} >
<CTNexaBold customStyle={styles.profileText} >Change Profile Photo</CTNexaBold>
<CustomImage
style={styles.containerCustomImage}
source={icons.arrowRight}
/>
</View>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 0.2, alignItems: 'center', justifyContent: 'center', marginBottom: 20 }} >
<TouchableOpacity style={styles.saveButton} >
<CTNexaBold customStyle={styles.saveButtonText} >SAVE</CTNexaBold>
</TouchableOpacity>
</View>
</View>
);
}
}
export default UploadProfilePicture;

React Native video wont play local file

I'm trying to play a video from my local file system the video players
shows up but the video wont even show on phone or it wont even play,
am i doing something wrong, i know exactly where my file is located
it just wont play its literally a video player with just a blank
screen.?
here is the code im using below showing a empty video player
import React from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import { Video } from 'expo';
import { MaterialIcons, Octicons } from '#expo/vector-icons';
export default class App extends React.Component {
state = {
mute: false,
shouldPlay: true,
}
handlePlayAndPause = () => {
this.setState((prevState) => ({
shouldPlay: !prevState.shouldPlay
}));
}
handleVolume = () => {
this.setState(prevState => ({
mute: !prevState.mute,
}));
}
render() {
const { width } = Dimensions.get('window');
return (
<View style={styles.container}>
<View>
<Text style={{ textAlign: 'center' }}> spiderman </Text>
<Video
source={{ uri: 'file://C:\Windows\System32\avenger\spiderman.mp4' }}
shouldPlay={this.state.shouldPlay}
resizeMode="cover"
style={{ width, height: 300 }}
isMuted={this.state.mute}
/>
<View style={styles.controlBar}>
<MaterialIcons
name={this.state.mute ? "volume-mute" : "volume-up"}
size={45}
color="white"
onPress={this.handleVolume}
/>
<MaterialIcons
name={this.state.shouldPlay ? "pause" : "play-arrow"}
size={45}
color="white"
onPress={this.handlePlayAndPause}
/>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
controlBar: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: 45,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: "rgba(0, 0, 0, 0.5)",
}
});
for local files you should use require not uri also I tried to reproduce your code but I'm not sure about file: syntax in React Native. so I instead used relative path
<Video
source={require('./utils/video_2018-08-16_14-12-06.mp4')}
shouldPlay={this.state.shouldPlay}
resizeMode="cover"
style={{ width, height: 300 }}
isMuted={this.state.mute}
/>
By the looks of it, the video is stored on a computer: source={{ uri: 'file://C:\Windows\System32\avenger\spiderman.mp4' }}
You haven't told us where the website is though. If it is a local website, it should work. But if it is on the web, it obviously won't work, as it is referring to a video that is stored on a computer, not on the internet. You need to upload that video to the website host, and change the source. This question isn't really CSS though.

About site display of React-native application

Site display on webview
Current practice, I am creating a browser application using React-native.
Google and other URLs will be displayed. However, the specific site is not displayed, it becomes a white screen.
※The address is https, and that site is displayed properly on PC or real machine Google Chrome.
Does this mean that there is a flaw in the SSL of the site?
please tell me.
We are doing site designation with source = {{uri: 'https://www.google.com/'}} in the code below.
##App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
const WEBVIEW_REF = "WEBVIEW_REF";
import React, { Component } from "react";
import { WebView } from "react-native";
import {
AppRegistry,
Platform,
StyleSheet,
Text,
View,
Button,
Image
} from "react-native";
const instructions = Platform.select({
ios: "Press Cmd+R to reload,\n" + "Cmd+D or shake for dev menu",
android:
"Double tap R on your keyboard to reload,\n" +
"Shake or press menu button for dev menu"
});
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
canGoBack: false,
canGoForward: false,
loading: false
};
}
render() {
return (
<View style={{ flex: 1 }}>
<View style={{ backgroundColor: "#FE9A2E", flex: 0.15 }}>
<Image
source={require("./header.png")}
style={{ alignSelf: "center" }}
/>
</View>
<View style={{ flex: 0.8 }}>
<WebView
ref={WEBVIEW_REF}
source={{ uri: "https://www.google.co.jp/" }}
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
/>
</View>
<View style={{ alignSelf: "center", flex: 0.08, flexDirection: "row" }}>
<View style={styles.buttonContainer}>
<Button
onPress={this.onBack.bind(this)}
title="←"
color="#FE9A2E"
/>
</View>
<View style={styles.buttonContainer}>
<Button
onPress={this.onReload.bind(this)}
title="↺"
color="#FE9A2E"
/>
</View>
<View style={styles.buttonContainer}>
<Button
onPress={this.onForward.bind(this)}
title="→"
color="#FE9A2E"
/>
</View>
</View>
</View>
);
}
onBack() {
this.refs[WEBVIEW_REF].goBack();
}
onForward() {
this.refs[WEBVIEW_REF].goForward();
}
onReload() {
this.refs[WEBVIEW_REF].reload();
}
onNavigationStateChange(navState) {
this.setState({
canGoBack: navState.canGoBack,
canGoForward: navState.canGoForward,
loading: navState.loading
});
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
welcome: {
fontSize: 20,
textAlign: "center",
margin: 10
},
instructions: {
textAlign: "center",
color: "#333333",
marginBottom: 5
},
buttonContainer: {
width: 100,
margin: 6
}
});
AppRegistry.registerComponent("App", () => App);
you should give a width a height style to your WebView tag. Try this and let me know.
add this hier in your webView tag style={styles.webViewStyles}, then add the styles below in your StyleSheet. webViewStyles:{height: 100, width: 100}, dont forget the comma before you add the styles in your StyleSheet.

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