Has anyone here ever done a vertical video Flatlist with a button that makes the video fullscreen (not the native UI). If yes could you give some advice on how to achieve it? Should I make the video position absolute and make it go from top to bottom (tried but couldn't make it work in the Flatlist)? Should I have a hidden video component that shares the state with the one in the Flatlist? Thanks any guidance is appreciated
I would recommend using modal feature from react navigation package(check this)
with this package you can render your full screen video inside a separate view.
Here you go.
const App: () => Node = () => {
const [popup, setPopup] = useState(false);
return (
<View style={{flex: 1, backgroundColor: 'aliceblue'}}>
<FlatList
data={[
{title: '1', url: 'https://www.w3schools.com/html/mov_bbb.mp4'},
{title: '2', url: 'https://www.w3schools.com/html/mov_bbb.mp4'},
]}
renderItem={({item}) => (
<Button title={item.title} onPress={() => setPopup(true)} />
)}
/>
{popup ? (
<View
style={{
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
left: 0,
backgroundColor: '#00000050',
}}>
<View
style={{
backgroundColor: '#FFFFFF50',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>Use whatever library you want to use to play the video</Text>
</View>
<Button title="Close" onPress={() => setPopup(false)} />
</View>
) : null}
</View>
);
};
I've built a sample that works for your requirement. Didn't do the Video stuff tho. Hope this helps.
Updated code with Popup component
const Popup = ({videoUrl, onClose}) => {
return (
<View
style={{
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
left: 0,
backgroundColor: '#00000050',
}}>
<View
style={{
backgroundColor: '#FFFFFF50',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>{videoUrl}</Text>
</View>
<Button title="Close" onPress={onClose} />
</View>
);
};
const App: () => Node = () => {
const [popup, setPopup] = useState(null);
return (
<View style={{flex: 1, backgroundColor: 'aliceblue'}}>
<FlatList
data={[
{title: '1', url: 'https://www.w3schools.com/html/mov_bbb.mp4'},
{title: '2', url: 'https://www.w3schools.com/html/mov_bbb.mp4'},
]}
renderItem={({item}) => (
<Button
title={item.title}
onPress={() =>
setPopup({
videoUrl: item.url,
})
}
/>
)}
/>
{popup != null ? (
<Popup videoUrl={popup.videoUrl} onClose={() => this.setPopup(null)} />
) : null}
</View>
);
};
What is exact problem? You can't make it fullscreen by custom button? Or you want to make it fullscreen with your own custom layout? I guess first.
Not clean example, but should show how it works.
https://snack.expo.dev/#valera.bitkovsky/react-native-flatlist-video-custom-fullscreen
import React from "react";
import { StyleSheet, Text, View, FlatList, Button } from "react-native";
import { Video, AVPlaybackStatus } from 'expo-av';
const VideoItem = React.forwardRef(({ url }, ref) => {
const video = React.useRef(null);
React.useImperativeHandle(ref, () => ({
full: () => {
video.current.presentFullscreenPlayer();
}
}), [])
return <Video
ref={video}
source={{
uri: url,
}}
style={{
width: 400,
height: 200
}}
useNativeControls
resizeMode="contain"
isLooping
/>
});
function App() {
const videoRefs = React.useRef([]);
return (
<View style={styles.app}>
<FlatList
data={[
{ url: "https://www.w3schools.com/html/mov_bbb.mp4" },
{ url: "https://www.w3schools.com/html/mov_bbb.mp4" }
]}
renderItem={({ item, index }) => (<View>
<VideoItem ref={ref => videoRefs.current[index] = ref} url={item.url} />
<Button title="Fullscreen" onPress={() => videoRefs.current[index].full()} />
</View>)}
/>
</View>
);
}
const styles = StyleSheet.create({
app: {
marginTop: 50,
marginHorizontal: "auto",
maxWidth: 500
}
});
export default App;
And if you want second varint, then you can just add state and change layout to absolute and do whatever you want.
UPD
Regarding the aproach where we use absolute styles, seems that isn't possible, see this issue
https://github.com/facebook/react-native/issues/29867
So, we still can use our custom controls, but probably we should use native fullscreen mode.
You can try use simple ScrollView, I know that it isn't optimazied for that very well, but absolute position should work
Related
I am trying to display an Image in which the data was fetch from an api and render into a flatlist, I am trying to concat or connect the sting link with the variable, in the image source uri but did not work, how can I fix it.
<FlatList
data = {data}
keyExtractor={item => item.id}
showsVerticalScrollIndicator = {false}
renderItem={({item}) => {
return (
<Image
source={{ uri: `https://placewave.com/avatar/${item.user_image}` }}
resizeMode="cover"
style={styles.userImage}
/>
)
}}
/>
Thanks for the help
Unfortunately it will not work in such way.
Image must not have conditional source prop. Consider moving https://placewave.com/avatar/${item.user_image} to a variable and add condition to display some kind of fallback(like ActivityIndicator) until you will have valid user_image.
Complete test code
import * as React from 'react';
import { Image, Text, View, StyleSheet, FlatList } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
const data = [{ id: 1, user_image: 'cute-boy-standing-little-white-background-40214306.jpg' }, { id: 1, user_image: 'no image url.jpg' }]
return (
<View >
<FlatList
data={data}
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => {
let uriVar
if (item.user_image) uriVar = `https://thumbs.dreamstime.com/b/${item.user_image}`
return (<View>
{uriVar != null ?
<Image source={{ uri: uriVar }}
style={styles.userImage}
/> : null} </View>)
}}
/>
<Text style={styles.paragraph}>
Hello
</Text>
</View>
);
}
const styles = StyleSheet.create({
userImage: {
width: '150px',
height: '150px',
borderWidth: 1
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
Thank you all for the respond, but I find the answer
First I define the url const URL = 'https://placewave.com/avatar';
Then Include it:
<Image
source={{ uri: URL + '/' + item.user_image}}
resizeMode="cover"
style={styles.userImage}
/>
import React,{useState} from 'react';
import axios from 'axios';
import { Video } from 'expo-av';
import { StyleSheet, View,Text, TextInput, ScrollView, Image, TouchableHighlight, Modal, Button } from 'react-native';
export default function App() {
const apiurl = 'https://api.rawg.io/api/games?page_size=5';
const [state, setState] = useState({
s: 'Enter a movie ...',
results: [],
selected: [],
});
const search = () => {
axios(apiurl + '&search=' + state.s).then(({data}) => {
let results = data.results;
console.log(results);
setState(prevState => {
return {
...prevState,
results: results,
};
});
});
};
const openPopup = slug => {
axios('https://api.rawg.io/api/games/' + slug).then(({data}) => {
let result = data;
console.log(result);
setState(prevState => {
return {...prevState, selected: result};
});
});
};
return (
<View>
<Text> Game Search</Text>
<TextInput
onChangeText={text =>
setState(prevState => {
return {
...prevState,
s: text,
};
})
}
value={state.s}
onSubmitEditing={search}
/>
<ScrollView>
{state.results.slice(0, 1).map(result => (
<TouchableHighlight
key={result.slug}
onPress={() => openPopup(result.slug)}>
<View>
<Image
source={{uri: result.background_image}}
style={{
width: 250,
height: 250,
alignItems: 'center',
justifyContent: 'center',
}}
resizeMode="cover"
/>
<Text>{result.name}</Text>
</View>
</TouchableHighlight>
))}
</ScrollView>
<Modal
animationType="fade"
transparent={false}
visible={typeof state.selected.name != 'undefined'}>
<ScrollView>
<View>
<Text>{state.selected.name}</Text>
<Image
source={{uri: state.selected.background_image}}
style={{
margin: 20,
width: '90%',
height: 300,
alignItems: 'center',
justifyContent: 'center',
}}
resizeMode="cover"
/>
<Video
source={{uri: state.selected.clip.clip}}
rate={1.0}
volume={1.0}
resizeMode="cover"
shouldPlay
isLooping
style={{width: 300, height: 300}}
/>
</View>
</ScrollView>
<Button
onPress={() =>
setState(prevState => {
return {...prevState, selected: {}};
})
}
title="Close"
/>
</Modal>
</View>
);
}
Whenever I run it, it gives me this error message for the clip otherwise don't get an error message for the image or name or anything else. Does anyone know how to solve it? It's a simple app and all the codes are in App.js
Here I have attached the image of the error that I am getting. I am using RawG API to create a simple Game-search app
Screenshot of that error
You need to handle the case where state.selected.clip is null or undefined. You can do that like this:
{
state.selected.clip && (
<Video
source={{uri: state.selected.clip.clip}}
rate={1.0}
volume={1.0}
resizeMode="cover"
shouldPlay
isLooping
style={{width: 300, height: 300}}
/>
);
}
Problems will occur when you haven't selected anything yet or if there is a game that doesn't have a clip. So you shouldn't render the Video component if this value is not properly set.
this.state={
task : "",
the_array : []
};
}
handleTextChange=(some_task) =>{
this.setState({
task : some_task
});
}
pushToList = () => {
let taskitem = {
name: this.state.task,
};
this.setState({
the_array: [...this.state.the_array, taskitem],
});
}
render() {
return(
<View style={{backgroundColor:'powderblue', height:'100%'}}>
<FlatList data = {this.state.the_array}
renderItem={(item) => <Text>{item.name}</Text>} keyExtractor={(item) => item.name} >
</FlatList>
<TextInput style={{ backgroundColor: 'lightsteelblue', height:60, borderRadius : 25, marginBottom:20}}
onChangeText={this.handleTextChange}>
</TextInput>
<TouchableOpacity style={{
backgroundColor: 'mediumpurple', height: 60, width: 80, alignSelf: 'center', borderRadius: 20, justifyContent: 'center',
alignItems: 'center', marginBottom:20
}} onPress={this.pushToList}>
This is my code.I'm trying to add Textinput content to Flatlist. For that purpose, I defined an object inside my button onPress method('pushToList'), named 'taskitem', and set a prop for it named 'name'.
'pushTolistMethod' is supposed to put the 'name' into the Flatlist on screen. but strangely it doesn't work and nothing happens when I press the button. I was wondering if anybody could help me with that.
Can you replace your flatlist code like this and try?
<FlatList data = {this.state.the_array}
renderItem={({ item }) => <Text>{item.name}</Text>} keyExtractor={(item) => item.name} >
</FlatList>
The data is on the item key so we use destructuring to access that from within the function.
I've used react-native modals https://github.com/react-native-community/react-native-modal. I'm trying to combine bottom half modal and modal slide from the sides using multiple modals. But while coming back from 2nd modal to 1st one, the modal goes down (closes) and then another opens. Please have a look at the videos below to see what I wanted to do.
What I'm trying to obtain with the modal
https://youtu.be/YaMjp_VJ_9Y
what is happening using react-native-modal
https://youtu.be/GR8otXHhElc
Code
export default class App extends Component<Props> {
state = {
visibleModal: null
};
renderButton = (text, onPress) => (
<TouchableOpacity onPress={onPress}>
<View style={styles.button}>
<Text>{text}</Text>
</View>
</TouchableOpacity>
);
renderModalContent = () => (
<View style={styles.modalContent}>
<Text>Hello!</Text>
{this.renderButton("Next Modal", () =>
this.setState({ visibleModal: 6 })
)}
{this.renderButton("Close", () => this.setState({ visibleModal: null }))}
</View>
);
renderNextModalContent = () => (
<View style={styles.modalContent}>
<Text>Hello from next modal!</Text>
{this.renderButton("BACK", () => this.setState({ visibleModal: 5 }))}
</View>
);
render() {
return (
<View style={styles.container}>
{this.renderButton("modal", () => this.setState({ visibleModal: 5 }))}
<Modal
isVisible={this.state.visibleModal === 5}
onBackButtonPress={() => this.setState({ visibleModal: null })}
style={styles.bottomModal}
>
{this.renderModalContent()}
</Modal>
<Modal
isVisible={this.state.visibleModal === 6}
animationIn="slideInLeft"
animationOut="slideOutRight"
onBackButtonPress={() => this.setState({ visibleModal: null })}
style={styles.bottomModal}
>
{this.renderNextModalContent()}
</Modal>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
bottomModal: {
// flex: 0,
justifyContent: "flex-end",
margin: 0
},
button: {
backgroundColor: "lightblue",
padding: 12,
margin: 16,
justifyContent: "center",
alignItems: "center",
borderRadius: 4,
borderColor: "rgba(0, 0, 0, 0.1)"
},
modalContent: {
backgroundColor: "white",
padding: 22,
justifyContent: "flex-end",
borderRadius: 4,
borderColor: "rgba(0, 0, 0, 0.1)"
}
});
I am afraid that modal should not be use in that way. From my perspective, what you are trying to archive can be done without using 2 modal
My suggestion
Make a component that will mount when you call modal out
In the component you will make 2 views which you will add animation to the
slidein view
So, when you press the trigger, the other view will just slidein inside the same modal
Hope this help!
I faced a similar issue in a project I had, that it bigger it got more and more modals were created in more and more screens and scenarios (without speaking on foreground notification etc... ),
So I ended up creating this package, for controlling all modals in my app with their own hierarchy
#idiosync/react-native-modal uses a hook interface, and doesn't use the additional native layer that react-native and react-native-modal implementations do.
This means that new modals just appear on new layers so you can add as many as you like.
You must ensure that you have an appropriate way to remove them from the component in question though!
https://www.npmjs.com/package/#idiosync/react-native-modal
Hey Everyone :) This is my first post here, hope I am doing everything correctly!
I am currently working on a school project and using react-native for some weeks now.
My Problem:
I have the file data.js:
const cardOne_1 = require("../images/Vergleiche/Eisbär.jpg");
const cardTwo_1 = require("../images/Vergleiche/Android.jpg");
const cardThree_1 = require("../images/Vergleiche/Han_Solo_Alt.jpg");
const cardOne_2 = require("../images/Vergleiche/Gorilla.jpg");
const cardTwo_2 = require("../images/Vergleiche/Apple.jpg");
const cardThree_2 = require("../images/Vergleiche/Han_Solo_Jung.jpg");
export default[
{
image: cardOne_1,
image2: cardOne_2,
text: '53%',
text2: '47%',
title: 'Icebear vs Gorilla',
check: false,
},
{
image: cardTwo_1,
image2: cardTwo_2,
text: '19%',
text2: '81%',
title: 'Android vs IOS',
check: true,
},
{
image: cardThree_1,
image2: cardThree_2,
text: '70%',
text2: '30%',
title: 'Han Solo',
check: false,
},
];
My Homescreen contains two of these Deckswipers (For better clarity I will show here only the code for the first one), which are used to compare two images:
Homescreen - With two DeckSwiper
import data from '../Data.js';
export default class SwipeCards2 extends Component {
_onSwipeLeft() {
this._deckSwiper1._root.swipeLeft();
this._deckSwiper2._root.swipeRight();
}
_onSwipeRight() {
this._deckSwiper2._root.swipeLeft();
this._deckSwiper1._root.swipeRight();
}
render() {
return (
<Container style={{ backgroundColor: '#ffffff' }}>
<View>
<DeckSwiper
ref={mr => (this._deckSwiper1 = mr)}
dataSource={data}
onSwipeRight={() => this._deckSwiper2._root.swipeLeft()}
onSwipeLeft={() => this._deckSwiper2._root.swipeRight()}
looping={true}
renderEmpty={() => (
<View style={{ alignSelf: 'center' }}>
<Text>Das war´s!</Text>
</View>
)}
renderItem={item => (
<Card
style={{
elevation: 3,
height: 335,
justifyContent: 'center',
width: Dimensions.get('window').width + 1,
marginLeft: -1,
marginTop: 0,
}}>
<TouchableWithoutFeedback onPress={() => this._onSwipeRight()}>
<CardItem cardBody style={{ alignItems: 'center' }}>
<Image
style={{
resizeMode: 'cover',
flex: 1,
height: 335,
}}
source={item.image}
/>
</CardItem>
</TouchableWithoutFeedback>
</Card>
)}
/>
</View>
</Container>
);
}
}
I want to set the state "check" in data.js to true, everytime the user does swipe to the right.
A Third Screen renders a List component, which should show the previous made decisions of the user. This list is based on "check" of data.js.
Screen 3 - List of all the decisions
I tried for almost three days and can not find any good solution!
Do you have any suggestions how to achieve this?
Thanks :)
I'm not sure how things work with this DeckSwiper component but since you are importing a static data, if you need to change the data you need to clone it and then change it. Assigning data clone to a state variable and then giving it to the component will reflect the changes to the component.
To change a property on a specific object in your array you also need an unique identifier like an ID or similar.
Example
import data from '../Data.js';
export default class SwipeCards2 extends Component {
constructor(props) {
super(props);
// clone the static data to state
this.state = {
data: [...data]
}
}
changingCheckFunction(obejctsUniqueId) {
this.setState((prevState) => {
// find the object's id
const itemIndex = prevState.data.findIndex(x => x.id == obejctsUniqueId);
// copy the item and assign the new checked value
const newItem = Object.assign({}, prevState.data[itemIndex], { checked: !prevState.data[itemIndex]});
// copy the previous data array
const newData = [...prevState.data];
// set the newItem to newData
newData[itemIndex] = newItem;
// return the new data value to set state
return { data: newData };
});
}
_onSwipeLeft() {
this._deckSwiper1._root.swipeLeft();
this._deckSwiper2._root.swipeRight();
}
_onSwipeRight() {
this._deckSwiper2._root.swipeLeft();
this._deckSwiper1._root.swipeRight();
}
render() {
return (
<Container style={{ backgroundColor: '#ffffff' }}>
<View>
<DeckSwiper
ref={mr => (this._deckSwiper1 = mr)}
dataSource={this.state.data}
onSwipeRight={() => this._deckSwiper2._root.swipeLeft()}
onSwipeLeft={() => this._deckSwiper2._root.swipeRight()}
looping={true}
renderEmpty={() => (
<View style={{ alignSelf: 'center' }}>
<Text>Das war´s!</Text>
</View>
)}
renderItem={item => (
<Card
style={{
elevation: 3,
height: 335,
justifyContent: 'center',
width: Dimensions.get('window').width + 1,
marginLeft: -1,
marginTop: 0,
}}>
<TouchableWithoutFeedback onPress={() => this._onSwipeRight()}>
<CardItem cardBody style={{ alignItems: 'center' }}>
<Image
style={{
resizeMode: 'cover',
flex: 1,
height: 335,
}}
source={item.image}
/>
</CardItem>
</TouchableWithoutFeedback>
</Card>
)}
/>
</View>
</Container>
);
}
}