I am trying to make my tab icons to render their screens when clicked upon and when they are not within the TabBar. It is working with IOS but not with Android. It seems that the tab selection range can only be reached within the TabBar and not outside, above that it is not attached to its icons. Is there any way to make it work outside the TabBar when clicking on its icon? Thanks
Another way I tried is by making the TabBar height at a 100% of the screen and making its backgroundColor Transparent to see the screen behind but it shows a white screen instead and hides the content behind it.
import React from 'react'
import {
StyleSheet,
Text,
View,
Image
} from 'react-native'
import {
createBottomTabNavigator,
createAppContainer
} from 'react-navigation'
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp
} from 'react-native-responsive-screen'
class DocSelection extends React.Component {
render() {
return ( <
View style = {
styles.container
} >
<
Text > CustomerService < /Text>
<
/View>
)
}
}
class Printing extends React.Component {
render() {
return ( <
View style = {
styles.container
} >
<
Text > hfhdfjedhfeehfjeh < /Text>
<
/View>
)
}
}
class Design extends React.Component {
render() {
return ( <
View style = {
styles.container
} >
<
Text > 874877847484787 < /Text>
<
/View>
)
}
}
const RouteConfigs = {
'Home': {
screen: DocSelection,
navigationOptions: { //tabBarButtonComponent: tabBarIcon: ({ tintColor, horizontal }) => (
<
Image style = {
{
margin: 15,
width: 35,
height: 35,
tintColor: "red"
}
}
source = {
require("../Icons/home.png")
}
/> ), }, }, 'Order history':{ screen: Printing, navigationOptions: { backgroundColor: '#262A2C', top:-60, borderTopWidth: 0, tabBarIcon: ({ tintColor }) => ( <
Image style = {
{
width: 32,
height: 32,
tintColor: "red"
}
}
source = {
require("../Icons/history-clock-button.png")
}
/> ), }, }, 'Customer service':{ screen: Design, navigationOptions: { tabBarIcon: ({ tintColor }) => ( <
Image style = {
{
top: 0,
margin: 15,
width: 40,
height: 40,
tintColor: "red"
}
}
source = {
require("../Icons/customer-service.png")
}
/> ), }, }, }; const DrawerNavigatorConfig = { intialRouteName: 'Home', navigationOptions: { tabBarVisible: true }, tabBarOptions: { tabStyle:{ top:-130, height:0 }, showLabel: false, style:{ backgroundColor:"rgba(255, 0, 0, 0)" }, }, }; const Navigator = createBottomTabNavigator(RouteConfigs, DrawerNavigatorConfig);
export default createAppContainer(Navigator);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
top: 300
}
});
If there was anyone interested in the answer, You just have to create a component and navigate to the concerned tab. Make sure the tab visibility is false and then place the component where you want. This practice will ensure that going back to previous tabs will save your previous state on previous pages.
If anyone is interested I can post an example. There is currently no reply so I will submit this answer instead.
I have structured it a little differently but here is a simpler case i hope would help you
example:
import React from 'react';
import {View,TouchableOpacity, PixelRatio, Image} from 'react-native'
import { createBottomTabNavigator, createAppContainer} from 'react-navigation'
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen'
class Home extends React.Component{
render(){
const {navigation} = this.props;
const { routeName } = navigation.state;
return(
<View style={{flex:1,
backgroundColor:'grey'}}>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Home")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Home" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Home'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Alt")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Alt" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Alt'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
</View>)}}
class Alt extends React.Component{
render(){
const {navigation} = this.props;
const { routeName } = navigation.state;
return(
<View style={{flex:1,
backgroundColor:'white'}}>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Home")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Home" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Home'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => {
navigation.navigate("Alt")
}}
style={ {position: 'relative',
width: PixelRatio.get() <= 2 ? 40 : 45,
height: PixelRatio.get() <= 2 ? 40 : 45,
alignItems: 'center',
justifyContent: 'center',
left: wp('5%'),
top: hp('11.5%'),
backgroundColor:routeName==="Alt" ? 'black' : 'white' ,
borderRadius: PixelRatio.get() <= 2 ? 40/2 : 45/2}}>
<Image
source={require("./Icons/category.png")}
//pay FlatIcon or design personal one
style={{ resizeMode: 'contain',
width: PixelRatio.get() <= 2 ? 25 : 30,
height: PixelRatio.get() <= 2 ? 25 : 30,
tintColor: routeName==='Alt'?'#81F018' : '#262A2C',
}}
/>
</TouchableOpacity>
</View>)}}
const AppTabNavigator = createBottomTabNavigator({
"Home": {
screen: Home,
},
"Alt": {
screen: Alt,
},
},
{
defaultNavigationOptions: {
tabBarVisible: false
},
}
)
export default createAppContainer(AppTabNavigator)
Related
Current behavior / Bug
As soon as I pause/play the video on custom video control or slide on slider the video keep stutter to 1 or 2 seconds later sometim even 3-5 seconds
Video Clip Bug
https://drive.google.com/file/d/1C9Nj7FMcgA-I-I5Zha5jdJ-IBGXYGWJL/view?usp=sharing
Reproduction steps
click button to enter the new screen
video autoplay as soon the new screen load (if not doobleTap or singleTap on overlay, it will play as normal)
singleTap on video's overlay for the custom controls to appear then click pause button to pause video.
click play button to play video again, then video stutter.
Sometime slide on slider to seek new video's currentTime, video stutter (rarely not stutter)
If you pause the video the slide and play again and custom video control fadeout, video will play as normal not stutter
Expected behavior
pause / play video not stutter.
slide on progress video and play video again not stutter
Platform
Which player are you experiencing the problem on:
Android
React Native Setup
react-native : 0.64.2
gradle: 6.9
JDK: 11
Android Studio: Dolphin 2021.3.1
Android SDK API: 30
build tools: 30.0.2
#react-native-community/slider: 4.3.1
react-native-video: 5.2.0
react-native-orientation-locker: 1.5.0
react-native-vector-icons: 8.1.0
Remote Video URL
I called video URL from google cloud storage by using redux-saga.
I already called dispatch function to called api from parent screen. So, in this screen I only useSelector to get URL from redux-saga.
Can use any video URL with .mp4 file to substitute it.
Video sample
https://drive.google.com/uc?export=download&id=1C9Nj7FMcgA-I-I5Zha5jdJ-IBGXYGWJL
Sample Code
import React, { useRef, useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { View, StyleSheet, BackHandler, Dimensions, TouchableNativeFeedback, Text, StatusBar, Platform } from 'react-native';
import Video from 'react-native-video';
import Orientation from 'react-native-orientation-locker';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Slider from '#react-native-community/slider';
import { normalize } from 'react-native-elements';
const { width, height } = Dimensions.get("window");
Icon.loadFont();
let overlayTimer;
let Timer;
const VideoPlayerScreen = (props) => {
let lastTap = null;
const dispatch = useDispatch();
const { navigation } = props;
const [Fullscreen, setFullscreen] = useState(false);
const [paused, setpaused] = useState(false);
const [currentTime, setcurrentTime] = useState(0);
const [duration, setduration] = useState(0.1);
const [overlay, setoverlay] = useState(false);
const playerRef = useRef();
const contract = useSelector((state) => state.contract.contract);
useEffect(() => {
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
return () => backHandler.remove();
}, [])
const backAction = () => {
// navigation.goBack();
return true;
}
const videoUri = contract.videoURL
const FullscreenToggle = () => {
if (Fullscreen) {
Orientation.lockToPortrait();
StatusBar.setHidden(false)
navigation.setOptions({ headerShown: true });
setFullscreen(false)
} else {
Orientation.lockToLandscape();
StatusBar.setHidden(true)
navigation.setOptions({ headerShown: false });
setFullscreen(true);
}
}
const handleDoubleTap = (doubleTapCallback, singleTapCallback) => {
const now = Date.now();
const DOUBLE_PRESS_DELAY = 300;
if (lastTap && (now - lastTap) < DOUBLE_PRESS_DELAY) {
clearTimeout(Timer);
doubleTapCallback();
} else {
lastTap = now;
Timer = setTimeout(() => {
singleTapCallback();
}, DOUBLE_PRESS_DELAY);
}
}
const ShowHideOverlay = () => {
handleDoubleTap(() => {
}, () => {
setoverlay(true)
overlayTimer = setTimeout(() => setoverlay(false), 5000);
})
}
const backward = () => {
playerRef.current.seek(currentTime - 5);
clearTimeout(overlayTimer);
overlayTimer = setTimeout(() => setoverlay(false), 3000);
}
const forward = () => {
playerRef.current.seek(currentTime + 5);
clearTimeout(overlayTimer);
overlayTimer = setTimeout(() => setoverlay(false), 3000);
}
const onslide = (slide) => {
playerRef.current.seek(slide * duration);
clearTimeout(overlayTimer);
overlayTimer = setTimeout(() => setoverlay(false), 3000);
}
const getTime = (t) => {
const digit = n => n < 10 ? `0${n}` : `${n}`;
const sec = digit(Math.floor(t % 60));
const min = digit(Math.floor((t / 60) % 60));
const hr = digit(Math.floor((t / 3600) % 60));
// return hr + ':' + min + ':' + sec;
return min + ':' + sec;
}
const load = ({ duration }) => setduration(duration);
const progress = ({ currentTime }) => setcurrentTime(currentTime);
return (
<View style={styles.container}>
{Platform.OS === 'android' ?
< View style={Fullscreen ? styles.fullscreenVideo : styles.video}>
<Video
source={{ uri: videoUri }}
style={{ ...StyleSheet.absoluteFill }}
ref={playerRef}
paused={paused}
repeat={true}
onLoad={load}
onProgress={progress}
resizeMode={"contain"}
rate={1.0}
/>
<View style={styles.overlay}>
{overlay ?
<View style={{ ...styles.overlaySet, backgroundColor: '#0006', alignItems: 'center', justifyContent: 'space-around' }}>
<View style={{ width: 50, height: 50 }}>
<Icon name='replay-5' style={styles.icon} onPress={backward} />
</View>
<View style={{ width: 50, height: 50 }}>
<Icon name={paused ? 'play-arrow' : 'pause'} style={styles.icon} onPress={() => setpaused(!paused)} />
</View>
<View style={{ width: 50, height: 50 }}>
<Icon name='forward-5' style={styles.icon} onPress={forward} />
</View>
<View style={styles.sliderCont}>
<View style={{ ...styles.timer, alignItems: 'center' }}>
<View style={{ flexDirection: 'row' }}>
<Text style={{ color: 'white' }}>{getTime(currentTime)}/</Text>
<Text style={{ color: 'white' }}>{getTime(duration)}</Text>
</View>
<View style={{ margin: 5 }}>
<Icon onPress={FullscreenToggle}
name={Fullscreen ? 'fullscreen' : 'fullscreen-exit'}
style={{ fontSize: 20, color: 'white' }} />
</View>
</View>
<Slider
style={{ margin: 5 }}
maximumTrackTintColor='white'
minimumTrackTintColor='white'
thumbTintColor='white'
value={currentTime / duration}
onValueChange={onslide}
/>
</View>
</View>
:
<View style={styles.overlaySet}>
<TouchableNativeFeedback onPress={ShowHideOverlay}><View style={{ flex: 1 }} /></TouchableNativeFeedback>
</View>
}
</View>
</View>
:
<View style={styles.video}>
<Video
source={{ uri: videoUri }}
style={{ width: width, aspectRatio: width / (height - normalize(110)) }}
controls
// ref={(ref) => {
// this.player = ref;
// }}
/>
</View>
}
</View >
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black'
},
// video: { width, height: width * .6, backgroundColor: 'black', justifyContent: 'center', alignItems: 'center' },
video: { width: "100%", aspectRatio: width / (height - normalize(80)), backgroundColor: 'black', alignItems: 'center', justifyContent: 'center' },
fullscreenVideo: {
width: "100%",
aspectRatio: 2 / 1,
backgroundColor: 'black',
...StyleSheet.absoluteFill,
elevation: 1
},
overlay: {
...StyleSheet.absoluteFillObject,
},
overlaySet: {
flex: 1,
flexDirection: 'row',
},
icon: {
color: 'white',
flex: 1,
textAlign: 'center',
textAlignVertical: 'center',
fontSize: 25
},
TextStyle: {
fontSize: 20, textAlign: 'center',
marginVertical: 100, color: '#6200ee', fontWeight: 'bold'
},
sliderCont: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0
},
timer: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 5
},
});
export default VideoPlayerScreen;
I'm in a React Native project that the client wants the product image scrolls from top to bottom vice versa in a modal, how can I achive this?
I already know how to solve this...
I had to create a counter that increase or decrease Y axis of ScrollView every 0.5 seconds and checking if reached the top or bottom.
In the modal component file:
import React, { useState, useEffect } from 'react';
import { StyleSheet, Modal, ScrollView, View, Image, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
import { Feather } from '#expo/vector-icons';
const ImageModal: React.FC<{ product: ProductType }> = ({ product }) => {
const [ axisY, setAxisY ] = useState<number>(0); // State that is used to know the current Y axis of ScrollView
const [ scrollToTop, setScrollToTop ] = useState<boolean>(false); // State that is used to checks if should go to top or bottom
// Handler that checks if ScrollView is scrolling to top or bottom
const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
// HELP: https://newbedev.com/detect-scrollview-has-reached-the-end
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; // Get scroll event properties
// If scrolling to top
if (scrollToTop) {
const isNearTop = contentOffset.y != 0; // Checks if Y axis reached the top of ScrollView
setScrollToTop(isNearTop); // Change the state value to FALSE, making ScrollView goes to bottom
} else {
const isNearBottom = layoutMeasurement.height + contentOffset.y >= contentSize.height; // Checks if Y axis reached the bottom of ScrollView
setScrollToTop(isNearBottom); // Change the state value to TRUE, making ScrollView goes to top
}
}
// Increase or decrease current Y axis every 0.5 seconds
useEffect(() => {
const timer = setInterval(() => {
setAxisY(prev => !scrollToTop ? prev + 1.5 : prev - 1.5);
}, 50);
return () => clearInterval(timer);
}, [scrollToTop]);
return (
<Modal
visible={ true }
transparent={ true }
statusBarTranslucent={ true }
animationType="fade"
>
<View style={ styles.container }>
<View style={ styles.box }>
<ScrollView
overScrollMode="never"
style={ styles.scroll }
scrollEnabled={ false }
showsVerticalScrollIndicator={ false }
contentOffset={{ x: 0, y: axisY }}
onScroll={ handleScroll }
>
<View style={ styles.imageBox }>
<Image source={{ uri: product.image_url }} style={ styles.image } />
</View>
</ScrollView>
<View>
<Text>Some random text!</Text>
</View>
</View>
<TouchableOpacity style={ styles.closeButton } onPress={ onClose }>
<Feather name="x" size={ 30 } color="#fff" />
</TouchableOpacity>
</View>
</Modal>
);
}
// Main Styles
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
padding: 20
},
closeButton: {
width: 60,
height: 60,
borderWidth: 2,
borderRadius: 12,
marginLeft: 20,
borderColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#58585a'
},
box: {
backgroundColor: '#fff',
width: '80%',
borderRadius: 10,
flexDirection: 'column'
},
scroll: {
width: '100%',
height: '80%',
borderBottomWidth: 1,
borderColor: '#58585a'
},
imageBox: {
width: '100%',
height: 600,
},
image: {
width: '100%',
height: '100%',
resizeMode: 'cover',
borderTopLeftRadius: 10,
borderTopRightRadius: 10
}
});
export default ImageModal;
I have been trying to get the blur effect to work on Android for a long time.
I tried to solve it using the viewRef reference system but it doesn't work right. Can somebody help me?
The sample page I wrote for Android is as follows;
import React, { useCallback, useEffect, useState, useRef } from 'react';
import { Image, StyleSheet, Text, TouchableOpacity, View, findNodeHandle, InteractionManager } from 'react-native';
import { BlurView } from '#react-native-community/blur';
const AndroidPreviewScreen = ({ route, navigation }) => {
const { speed, brightness, selectedDevice, selectedAnimationId, selectedUserTheme, selectedThemeId, selectedEqAnimation, selectedColors } = route.params;
const selectedAnimation = selectedAnimationId && animationOptions.find(o => o.id === selectedAnimationId);
const selectedTheme = selectedThemeId && themeOptions.find(o => o.id === selectedThemeId);
const [isVisible, setIsVisible] = useState(false);
const [viewRef, setViewRef] = useState(null);
const blurRef = useRef();
useEffect(() => {
setTimeout(() => {
setIsVisible(true);
}, 50);
}, [])
const onBack = useCallback(() => {
setIsVisible(false);
setTimeout(() => {
vibrateLight();
navigation.goBack();
}, 5);
}, []);
return (
<View style={styles.container} >
<TouchableOpacity style={styles.closeBtn} onPress={onBack} hitSlop={hitSlop.mid}>
<FontAwesome5Icon name="times" color={colors.powerCustomBg} size={20} style={styles.closeIcon} />
</TouchableOpacity>
{isVisible && <View style={styles.previewImageContainer}>
{selectedDevice && !selectedDevice.isOn && <View style={styles.offContainer}>
<FontAwesome5Icon name={'info-circle'} size={20} color={colors.powerBtnOn} />
<Text style={styles.offText}>Off</Text>
</View>}
<View style={[styles.colorMaskContainer]}
ref={n => {
if (n && viewRef === null) {
setViewRef(findNodeHandle(n))
}
}}
collapsable={false}
>
// I want to blur this section.
</View>
{console.log("viewRef : : ", viewRef)}
{viewRef && <BlurView
style={styles.blur}
viewRef={viewRef}
blurType={"light"}
blurRadius={20}
blurAmount={20}
downsampleFactor={5}
overlayColor={"light"}
// reducedTransparencyFallbackColor="white"
/>
}
<Image resizeMethod="auto" resizeMode="cover" source={{ uri: 'asset:/images/images/imageMy.webp' }} style={[styles.previewImage]} />
</View>}
</View >
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.black,
zIndex: 9999999
},
closeBtn: {
flexDirection: 'row',
position: 'absolute',
right: 15,
top: deviceHeight * 0.13 - 75,
zIndex: 9999,
backgroundColor: colors.noThemeBg,
width: 36,
height: 36,
borderRadius: 20,
alignItems: 'center',
alignContent: 'center',
justifyContent: 'center'
},
closeIcon: {
textAlign: 'center'
},
previewImageContainer: {
flex: 1,
width: '100%',
alignSelf: 'center',
flexDirection: 'column',
overflow: 'hidden',
borderColor: colors.mainBgMid,
},
offContainer: {
position: 'absolute',
top: 20,
flexDirection: 'row',
alignSelf: 'center',
zIndex: 99
},
offText: {
color: colors.powerBtnOn,
fontFamily: 'Montserrat-SemiBold',
fontSize: 14,
lineHeight: 16,
alignSelf: 'center',
marginLeft: 10
},
colorMaskContainer: {
position: 'absolute',
top: '15%',
height: '65%',
width: '100%',
backgroundColor: "#000",
},
colorMask: {
position: 'absolute',
alignSelf: 'center',
width: '100%',
backgroundColor: "#000",
},
blur: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
},
previewImage: {
position: 'absolute',
height: '100%',
width: '100%',
}
});
export default AndroidPreviewScreen;
I want to blur where I try to refer to viewRef. The reference value I gave in BlurView points to that View.
Actual Behaviour :
I am supposed to implement signature pad in landscape-right mode along with a timestamp of signature drawn. Then take a screenshot of the view, and save it in document directory (iOS) or external directory (Android) in portrait mode by rotating it. I was successful in implementing signature screen in landscape-right mode using transform: [{rotate: '90deg"}] css property, and react-native-signature-capture, save the captured screenshot of signature along with the timestamp of signature drawn in local directory using react-native-view-shot and convert it into base64 format using react-native-fs.
But the saved screenshot is not in portrait mode and I'm trying to rotate the image while saving it in document directory (iOS) or external directory (Android) without using any modules. I also tried rotating the image while saving it using canvas context API but could not find way to access canvas in react-native to rotate image while saving it as canvas is HTML DOM related.
Expected Behaviour :
I'm supposed to save the signature drawn along with timestamp in document directory (iOS) or external directory (Android) in portrait mode as shown in below screenshot.
Additional Resources :
Code :
render() {
return (
<View
style={{
flex: 1,
flexDirection: 'row',
overflow: "hidden",
}}>
<StatusBar hidden={true} />
<View
style={{
flex: 0.8,
flexDirection: 'row-reverse',
marginVertical: width / 18,
overflow: "hidden",
}}>
<ViewShot
ref="viewShot"
style={[styles.viewShot, { transform: [{ rotate: this.state.bool && '90deg' }] }]}>
{/* options={{ width: height, height: width }}> */}
<SignatureCapture
style={styles.signature}
ref={sign => (this.signComponent = sign)}
onSaveEvent={this._onSaveEvent}
onDragEvent={this._onDragEvent}
saveImageFileInExtStorage={true}
showNativeButtons={false}
showTitleLabel={false}
showBorder={false}
viewMode={'portrait'}
square={true}
backgroundColor={"white"}
maxSize={800}
rotateClockwise={!!true}
/>
<View
ref="timeRef"
style={{
width: width / 10,
height: width / 3,
justifyContent: 'flex-end',
flexDirection: 'row-reverse',
}}>
<View
style={{
width: width / 1.8,
height: width / 1.8,
transform: [{ rotate: '-90deg' }],
overflow: "hidden",
paddingLeft: width / 18,
paddingTop: width / 25
}}>
<Text style={styles.time}>{this.state.data}</Text>
</View>
</View>
</ViewShot>
<Image
ref="imageRef"
source={{ uri: this.state.imageUri }}
style={{ transform: [{ rotate: '90deg' }] }}
/>
</View>
<View
style={{
flex: 0.2,
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'column',
overflow: "hidden",
backgroundColor: Colors.white,
}}>
<View
style={{
backgroundColor: Colors.darkGreen,
width: width / 2,
justifyContent: 'center',
alignItems: 'center',
paddingRight: width / 25,
paddingVertical: width / 37.5,
transform: [{ rotate: '-90deg' }],
overflow: "hidden",
}}>
<TouchableOpacity
hitSlop={{ top: 30, left: 50, right: 50, bottom: 30 }}
onPress={() => {
this.saveSign();
}}>
<Text style={{ fontSize: width / 18, color: Colors.white }}>Submit </Text>
</TouchableOpacity>
</View>
<View
style={{
backgroundColor: '#5476ab',
width: width / 2,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: width / 37.5,
transform: [{ rotate: '-90deg' }],
overflow: "hidden",
}}>
<TouchableOpacity
hitSlop={{ top: 30, left: 50, right: 50, bottom: 30 }}
onPress={() => {
this.resetSign();
}}>
<Text style={{ fontSize: width / 18, color: Colors.white }}>Clear</Text>
</TouchableOpacity>
</View>
<View
style={{
backgroundColor: '#73c5de',
width: width / 2,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10,
transform: [{ rotate: '-90deg' }],
}}>
<TouchableOpacity
hitSlop={{ top: 30, left: 50, right: 50, bottom: 30 }}
onPress={() => {
this.onCancel();
}}>
<Text style={{ fontSize: width / 18, color: Colors.white }}>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
_onSaveEvent(result) {
this.setState({ signature: result.pathName,
markResult: result.encoded });
}
_onDragEvent() {
this.setState({ dragged: true });
}
saveSign() {
if (this.state.dragged === true) {
this.setState({ bool: true });
this.refs.viewShot.capture().then(uri => {
this.setState({ imageUri: uri });
console.log("uri123", uri);
RNFS.readFile(this.state.imageUri,
'base64').then(image => {
console.log("image123", image);
this.setState({ sign: image }, () => {
this.ChangeOrientation();
});
});
});
} else {
Alert.alert('NALG', 'Please sign the signature
pad to submit');
}
ChangeOrientation() {
this.props.getSignature(this.state.sign);
this.props.setModalVisible(!this.props.modalVisible);
}
Screenshot of Actual Behaviour :
Screenshot of Expected Behaviour :
Environment:
react-native : 0.61.1
react-native-view-shot : ^3.0.2
react-native-signature-capture : ^0.4.10
react-native-fs : ^2.16.2
const showImagePicker = () => {
const options = {
title: i18n.t('issueReport.form.photoSelection'),
storageOptions: {
skipBackup: true,
path: 'images',
},
allowsEditing: true,
};
ImagePicker.showImagePicker(options, response => {
const { originalRotation } = response
console.log('Response = ', response);
console.log('originalRotation = ', originalRotation);
if (originalRotation === 90) {
setOriginalRotation(90)
} else if (originalRotation === 270) {
setOriginalRotation(-90)
}
setSelectedImageFile(response);
if (response && response.height === 3000) {
setLandscape(true)
}
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
} else {
const source = { uri: response.uri };
console.log('Image Path', source);
// You can also display the image using data:
// const source = { uri: 'data:image/jpeg;base64,' + response.data };
setSelectedImage(source);
setPhotoSelectVisible(true);
}
});
};
For more detail
https://github.com/react-native-image-picker/react-native-image-picker/issues/655
I am creating an application for android and ios in react-native(0.57.7) and using react-native-video to play videos uploaded into vimeo. After integrate react video plugin, tested in both device. In ios it works perfectly but in android, I am not able to play video in full-screen mode. Here is my code for Android:
import React, { PureComponent } from 'react';
import {
View,
Text,
Image,
ImageBackground,
StyleSheet,
SafeAreaView,
TouchableOpacity,
ActivityIndicator
} from 'react-native';
import PropTypes from 'prop-types'
import Video from "react-native-video";
import Orientation from 'react-native-orientation-locker';
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from '../components/Resposive';
import RF from "react-native-responsive-fontsize"
export default class Videoplayer extends PureComponent {
constructor(props){
super(props);
this.state = {
loading: true,
videoFileArr:[],
showPlayer:false,
playing:false
}
}
componentDidMount() {
Orientation.lockToLandscape();
this.fetchVimeoVideo();
}
componentWillUnmount() {
Orientation.lockToPortrait();
}
goToHome(){
Orientation.lockToPortrait();
this.props.navigation.goBack();
}
fetchVimeoVideo = async () => {
await this.setState({ loading: true });
const { navigation } = this.props;
const jsonReceived = navigation.getParam('item', {})
const url = "https://api.vimeo.com/me/videos/" + jsonReceived.video_link
console.log(url)
const response = await fetch(
url, {
method: "get",
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization:"Bearer f9e937d64037e657addcf088f28e6cb5"
}
});
const jsonResponse = await response.json();
const { files} = jsonResponse;
if (response.status != 200) {
alert(response.status)
}
console.log(files)
await this.setState({ videoFileArr:files, loading: false });
};
renderOptions = () => {
if (this.state.loading === true) {
return (
<View style={{
flex: 1,
alignItems: "center",
justifyContent: "center"
}}>
<ActivityIndicator size="large" color="#00ff00" />
<Text style={{ fontFamily: "Futura Std", fontSize: RF(3.0), fontWeight: "900", color: "#244E25", textAlign: "center" }}>Please wait while we are loading questions for you</Text>
</View>
)
}else if (this.state.videoFileArr.length> 0 && this.state.playing === false) {
const { navigation } = this.props;
const jsonReceived = navigation.getParam('item', {})
return(
<ImageBackground style={{width:"100%",height:"100%",alignItems:"center",justifyContent:"center"}} source={{uri:jsonReceived.video_banner}}>
<TouchableOpacity
onPress={() => {
this.setState({playing:true})
}}
>
<Image source={require("../assets/Common/Play/playIcon.png")}/>
</TouchableOpacity>
</ImageBackground>
)
} else if (this.state.videoFileArr.length > 0 && this.state.playing === true) {
return (
<View style={styles.container}>
<Video source={{ uri:this.state.videoFileArr[0].link}} // Can be a URL or a local file.
ref={ ref =>
this.player = ref
} // Store reference
onBuffer={this.onBuffer} // Callback when remote video is buffering
onError={this.videoError} // Callback when video cannot be loaded
style={styles.backgroundVideo}
controls={true}
paused={false}
fullscreen={true}
/>
</View>
)
}
}
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, overflow: "hidden" }}>
<View style={{ flex: 1, backgroundColor: "green" }}>
{this.renderOptions()}
</View>
{/* top navigationBar */}
<View
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
width: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
height: 80,
backgroundColor: null
}}
>
<TouchableOpacity onPress={
()=>this.goToHome()
}>
<Image style={{ margin: 8 }} source={require("../assets/Common/goBack/goBack.png")} />
</TouchableOpacity>
<TouchableOpacity>
<Image style={{ margin: 8 }} source={require("../assets/Common/Star/starOff.png")} />
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container:{ flex: 1, justifyContent: "center"},
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
and this is the output screen where I can not play video in full screen:
Please help, What I'm doing wrong ?
I have solved fullscreen of video frame just adding resizeMode on Video component:
<Video source={{ uri:this.state.videoFileArr[0].link}} // Can be a URL or a local file.
ref={ ref =>
this.player = ref
} // Store reference
onBuffer={this.onBuffer} // Callback when remote video is buffering
onError={this.videoError} // Callback when video cannot be loaded
style={styles.backgroundVideo}
controls={true}
paused={false}
fullscreen={true}
resizeMode="cover"
/>
I was struggling with the same problem couple of days ago but I made it work for me in android. I hope this will help you also.
You will need to install some other package as well that is
1. react-native-video-controls
2. react-native-orientation
now In your Screen where the video will be played.
import React, { Component } from 'react'
import {
Text,
StyleSheet,
StatusBar,
Dimensions,
Alert,
Modal,
BackHandler,
TouchableOpacity,
ToastAndroid,
} from 'react-native'
import { Container, View, Button, Icon, List, ListItem } from 'native-base';
import Video from 'react-native-video';
import Orientation from 'react-native-orientation';
const sample = require('../assets/demo.mp4');
export default class Player extends Component {
constructor(props) {
super(props);
this.onLoad = this.onLoad.bind(this);
this.onProgress = this.onProgress.bind(this);
}
state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'contain',
duration: 0.0,
currentTime: 0.0,
active: false,
modalVisible: false,
fullScreen: true,
};
onLoad(data) {
this.setState({ duration: data.duration });
}
onProgress(data) {
this.setState({ currentTime: data.currentTime });
}
getCurrentTimePercentage() {
if (this.state.currentTime > 0) {
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
} else {
return 0;
}
}
renderRateControl(rate) {
const isSelected = (this.state.rate == rate);
return (
<ListItem>
<TouchableOpacity onPress={() => { this.setState({ rate: rate }) }}>
<Text style={{ fontWeight: isSelected ? "bold" : "normal" }}>
{rate}x
</Text>
</TouchableOpacity>
</ListItem>
)
}
renderResizeModeControl(resizeMode) {
const isSelected = (this.state.resizeMode == resizeMode);
return (
<TouchableOpacity onPress={() => { this.setState({ resizeMode: resizeMode })
}}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? "bold" :
"normal" }]}>
{resizeMode}
</Text>
</TouchableOpacity>
)
}
setModalVisible = (visible) => {
this.setState({ modalVisible: visible });
}
fullScreen = () => {
Orientation.getOrientation((err, orientation) => {
if (orientation == 'LANDSCAPE') {
Orientation.lockToPortrait();
} else {
Orientation.lockToLandscape();
}
});
}
backAction = () => {
Orientation.getOrientation((err, orientation) => {
if (orientation == 'LANDSCAPE') {
Orientation.lockToPortrait();
}
});
};
componentDidMount() {
this.backHandler = BackHandler.addEventListener(
"hardwareBackPress",
this.backAction
);
}
componentWillUnmount() {
this.backHandler.remove();
}
render() {
const { modalVisible, paused } = this.state
const url = `https://www.sample-
videos.com/video/mp4/720/big_buck_bunny_720p_10mb.mp4`;
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
return (
<View style={styles.container}>
<StatusBar hidden={true} />
<Video source={sample}
style={styles.fullScreen}
rate={this.state.rate}
paused={this.state.paused}
volume={this.state.volume}
muted={this.state.muted}
resizeMode={this.state.resizeMode}
onLoad={this.onLoad}
onProgress={this.onProgress}
onEnd={() => { alert('Done!') }}
controls
repeat={true} />
<View style={[{ left: 0 }, styles.rateControl]}>
<Button
transparent
onPress={() => {
this.fullScreen();
}}
>
<Icon type="FontAwesome5" name="compress" style={{ color: "#fff",
fontSize: 15 }} />
</Button>
</View>
<View style={styles.rateControl}>
<Button
transparent
onPress={() => {
this.setModalVisible(true);
}}
>
<Icon type="FontAwesome5" name="ellipsis-v" style={{ color:
"#fff", fontSize: 15 }} />
</Button>
</View>
{/* <View style={styles.controls}>
<View style={styles.generalControls}>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
</View>
</View>
<View style={styles.trackingControls}>
<View style={styles.progress}>
<View style={[styles.innerProgressCompleted, { flex: flexCompleted }]} />
<View style={[styles.innerProgressRemaining, { flex: flexRemaining }]} />
</View>
</View>
</View> */}
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<View style={styles.closeModal}>
<Button
transparent
onPress={() => { this.setModalVisible(!modalVisible);
}}
>
<Icon name="close" />
</Button>
</View>
<View>
<Text style={{ textAlign: 'center', fontWeight: 'bold'
}}>Play Rate</Text>
<List style={{ flexDirection: 'row', justifyContent:
'space-between', alignItems: 'center' }}>
{this.renderRateControl(0.25)}
{this.renderRateControl(0.5)}
{this.renderRateControl(1.0)}
{this.renderRateControl(1.5)}
{this.renderRateControl(2.0)}
</List>
</View>
</View>
</View>
</Modal>
</View >
)
}
}
const styles = StyleSheet.create({
backgroundVideo: {
// position: 'absolute',
// top: 0,
// left: 0,
// bottom: 0,
// right: 0,
width: Dimensions.get('window').width,
height: Dimensions.get('window').width * .6,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black',
},
fullScreen: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
controls: {
backgroundColor: "transparent",
borderRadius: 5,
position: 'absolute',
bottom: 20,
left: 20,
right: 20,
},
progress: {
flex: 1,
flexDirection: 'row',
borderRadius: 3,
overflow: 'hidden',
},
innerProgressCompleted: {
height: 20,
backgroundColor: '#cccccc',
},
innerProgressRemaining: {
height: 20,
backgroundColor: '#2C2C2C',
},
generalControls: {
flex: 1,
// flexDirection: 'row',
borderRadius: 4,
overflow: 'hidden',
paddingBottom: 10,
},
rateControl: {
flexDirection: 'row',
position: 'absolute',
top: 10,
right: 10
},
volumeControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
resizeModeControl: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
controlOption: {
alignSelf: 'center',
fontSize: 11,
color: "white",
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
centeredView: {
flex: 1,
marginTop: '22%'
},
modalView: {
width: '100%',
padding: '5%',
backgroundColor: "white",
position: 'absolute',
bottom: 10,
},
openButton: {
backgroundColor: "#F194FF",
borderRadius: 20,
padding: 10,
elevation: 2
},
closeModal: {
alignItems: 'flex-end',
margin: -10
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center"
},
modalText: {
marginBottom: 15,
textAlign: "center"
}
});
I hope this will help you.
Most of suggestion involves adding additional libraries to achieve fullscreen in react-native-vide for android. Its not really needed.
What we need is set the height and width of to full screen to achieve this. we need to use variable instead of fixed value then setState to refresh the view.
Below is the sample typescript code that works for react-native-video fullscreen:
import React, {Component} from 'react';
import {
View,
StyleSheet,
TouchableWithoutFeedback,
Dimensions,
} from 'react-native';
import Video from 'react-native-video';
import Icon from 'react-native-vector-icons/FontAwesome';
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';
interface Props {}
interface States {
paused: boolean;
inFullScreen: boolean;
}
export default class Sandbox extends Component<Props, States> {
player: any;
videoStyle: {minWidth: number; minHeight: number};
constructor(props: Props) {
super(props);
this.state = {
paused: true,
inFullScreen: false,
};
this.videoStyle = {minWidth: 400, minHeight: 400};
}
render() {
return (
<View style={{height: 400, width: 400}}>
<Video
source={{
uri:
'https://www.radiantmediaplayer.com/media/big-buck-bunny-360p.mp4',
}} // Can be a URL or a local file.
ref={(ref: any) => {
this.player = ref;
}} // Store reference
controls={false}
paused={this.state.paused}
resizeMode={'cover'}
style={this.videoStyle}
/>
<View style={styles.controls}>
<TouchableWithoutFeedback
onPress={() => {
this.setState({paused: !this.state.paused});
}}>
<Icon name={'play'} size={30} color="#FFF" />
</TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => {
if (!this.state.inFullScreen) {
//Orientation.lockToLandscape();
this.videoStyle = {
minHeight: Dimensions.get('window').height,
minWidth: Dimensions.get('window').width,
};
} else {
this.videoStyle = {
minHeight: 400,
minWidth: 400,
};
}
this.setState({inFullScreen: !this.state.inFullScreen});
}}>
<MaterialIcon name={'fullscreen'} size={30} color="#FFF" />
</TouchableWithoutFeedback>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
controls: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
height: 48,
top: 20,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
paddingHorizontal: 10,
},
});
As #Selva answered we can use variables for video sizes i.e width & height and make it occupy full screen and use a stack to place the video in a stack screen above the current screen and pop it when needed. Even if it's in a flat list.
Had same issue fix it by removing the wrapping and setting just the height to the player, no other library needed and no need to manage the orientation.
<Video source={{uri: this.props.videoData.uri}}
onEnd={this.onVideoEnd}
style={styles.player}
controls={true} />
<View
onStartShouldSetResponder={() => setPaused(!paused)}
style={{
marginHorizontal: 10,
backgroundColor: "black",
position: "relative",
transform: [{ rotate: "90deg" }],
// justifyContent:'center',
// alignItems:'center'
}}
>
<Video
onEnd={onEnd}
onLoad={onLoad}
onLoadStart={onLoadStart}
posterResizeMode={"cover"}
onProgress={onProgress}
paused={paused}
ref={(ref) => (videoPlayer.current = ref)}
resizeMode={'stretch'}
source={{
uri: "url",
}}
style={{
...styles.backgroundVideo,
height: width,
aspectRatio:2
// width: "100%"
// alignSelf:'center'
// transform: [{ rotate: '90deg'}]
}}
/>
just give it a width of
width:'100%'