I'm able to console log the viewable items, when swiping when using FlatList, but I was wondering how I can manage pausing the video. Or if there's a better way in doing so?
This is the RenderItem function component
const RenderItem = (props) => {
const [paused, setPaused] = useState(true);
const togglePlay = () => setPaused(prev => !prev);
return (
<View>
{props.is_video ? (
<>
<Video
paused={paused}
repeat
source={{uri: props.mediaUrl}}
/>
<TouchableWithoutFeedback onPress={togglePlay}>
<View>
{paused ? <Icon
size={180}
name="play"
type="FontAwesome"
style={{opacity: 0.7, color: '#cccccc'}}
/> : null}
</View>
</TouchableWithoutFeedback>
</>
) : (
<Image source={{uri: props.mediaUrl}} />
)}
</View>
);
};
Then in another function, I have this:
const Post = (props) => {
const onViewRef = useRef((viewableItems)=> {console.log(viewableItems)});
const viewConfigRef = useRef({ viewAreaCoveragePercentThreshold: 50 });
return (
<View style={{flex: 1}}>
<View>
<FlatList
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
data={props.navigation.state.params.media}
snapToAlignment={'center'}
horizontal
decelerationRate={'fast'}
pagingEnabled
renderItem={({item}) => <RenderItem {...item} />}
keyExtractor={item => item.mediaUrl}
/>
</View>
</View>
);
};
I'm able to press the video and it'll Play or Pause. When I'm swiping right now, the videos continue to play. I want to be able to ensure that when swiping, the video played will become paused now.
You can lift the pause/playing state to the parent component (Post). Since at most 1 video should be playing at anytime, the state can simply store the item ID (or mediaUrl if you are using that as the key/ID) that is currently playing.
In RenderItem:
<Video paused={props.paused} ... />
<TouchableWithoutFeedback onPress={props.onTogglePlay}>
In Post (you can use useCallback for the anonymous functions):
const [activeVideo, setActiveVideo] = useState(null);
...
<FlatList onViewableItemsChanged={() => setActiveVideo(null)} .../>
...
<RenderItem
paused={activeVideo !== item.mediaUrl}
onTogglePlay={() =>
setActiveVideo(item.mediaUrl === activeVideo ? null : item.mediaUrl)
}
{...item}
/>
You may also store the ref to the active video and pause the video through that ref.
Related
I'm using react-native-snap-carousel and I have large data, and it feel quite laggy when swiping each time.
I debugged the renderItem method and it's rendering each time on swipe. Should this happen normally or what ?
Is there a way to solve this issue ?
const [activeSlide, setActiveSlide] = useState(0);
const renderItem = useCallback(
({ item, index }) => {
return (
<CarouselImage
ad={ad}
item={item}
index={index}
showImage={showImage}
/>
);
},
[ad, showImage]);
return ad?.videos?.length > 0 || ad?.images?.length > 0 ? (
<View style={styles.container}>
<Carousel
initialNumToRender={selectedItems.length}
maxToRenderPerBatch={5}
ref={carouselRef}
swipeThreshold={5}
itemWidth={wp(375)}
data={selectedItems}
sliderWidth={wp(375)}
enableMomentum={false}
lockScrollWhileSnapping
renderItem={renderItem}
onSnapToItem={(index) => setActiveSlide(index)}
/>
<Pagination
activeOpacity={1}
tappableDots={true}
animatedDuration={100}
inactiveDotScale={0.4}
inactiveDotOpacity={0.4}
carouselRef={carouselRef}
dotStyle={styles.dotStyle}
activeDotIndex={activeSlide}
dotsLength={selectedItems.length}
containerStyle={styles.pagination}
dotContainerStyle={styles.dotContainer}
inactiveDotStyle={styles.inactiveDotStyle}
/>
</View>
I have two videos from https://www.npmjs.com/package/react-native-video
I do not have the controls active, my intention is to activate and deactivate the videos by pressing buttons. I do it with states, my problem is that when I press a button to pause or play a video, all of them play, not just one.
I have a list of videos in a JSON and iterate through all of them.
Here is a snippet of my code:
const [paused, setPaused] = useState(false);
const playVideo = () => {
setPaused(!paused);
}
{videos.map((video) => (
<Video
source={{ uri: video.video }}
rate={1.0}
volume={1.0}
resizeMode="cover"
style={styles.video}
paused={paused}
onEnd={() => setPaused(true)}
/>
{paused && (
<View style={styles.videoPause}>
<Text style={styles.title}>{video.titulo}</Text>
<Text style={styles.description}>{video.descripcion}</Text>
<TouchableOpacity style={styles.playButton} onPress={() => playVideo()}>
<CustomIcon name="play" size={90} color={'#fff'} />
</TouchableOpacity>
</View>
)}
))}
The problem is with your state, you should make a component to wrap the Video and manage play/pause state in there. This way you can control every video individually.
First create a component which has independent state and not global as you have it here.
Example:
export default function VideoPlayer(props) {
const { styles, video } = props
const [paused, setPaused] = useState(false)
const playVideo = () => {
setPaused(!paused)
}
return (
<View>
<Video
paused={paused}
rate={1.0}
resizeMode="cover"
source={{ uri: video.video }}
style={styles.video}
volume={1.0}
onEnd={() => setPaused(true)}
/>
{
paused && (
<View style={styles.videoPause}>
<Text style={styles.title}>{video.titulo}</Text>
<Text style={styles.description}>{video.descripcion}</Text>
<TouchableOpacity style={styles.playButton} onPress={() => playVideo()}>
<CustomIcon color="#fff" name="play" size={90} />
</TouchableOpacity>
</View>
)
}
}
)
and then render it in your page like this:
{videos.map((video) => <VideoPlayer styles={...YOUR_STYLES} video={video}/>}
You can also declare the styles inside the component if you prefer but then you wont need to pass them as props.
i have two different API and i want show two API in one Flatlist this is worked (look the picture). i show it using filter by id API (if API have same id will show). My question is how to remove/hide/dont show null value flatlist (look the picture)?
Im using API from https://jsonplaceholder.typicode.com
picture my app
const {user, post} = useSelector(state => state.reducer);
const dispatch = useDispatch();
const getData = [...user, ...post];
useEffect(() => {
dispatch(getProfile());
dispatch(getPost());
}, []);
const tailwind = useTailwind();
const renderPost = ({item}) => {
const renUsr = user.filter(renUsr => renUsr.id === item.userId);
return (
renUsr.id !== item.userId ? (
<View style={tailwind('pb-4')}>
<View style={tailwind('px-4 py-4 bg-gray-200 mx-6 rounded-[20px]')}>
<View style={tailwind('flex flex-row')}>
<Image style={tailwind('rounded bg-black w-8 h-8')} />
{renUsr.map(posting => (
<Text
key={posting.id}
style={tailwind('pl-2 font-semibold py-2')}>
{posting.name}
</Text>
))}
</View>
<View style={tailwind('mt-2')}>
<TouchableHighlight
style={styles.touchHighlight}
onPress={navigation}>
<View style={tailwind('bg-gray-200 p-1')}>
<Text key={item.id}>{item.body}</Text>
</View>
</TouchableHighlight>
</View>
</View>
</View>
) : (
null
)
);
};
return (
<FlatList
data={getData}
renderItem={renderPost}
keyExtractor={item => item.id}
/>
);
};
user.filter(renUsr => renUsr.id === item.userId);
Instead of using the filter here, just filter the list passed to the flatlist so you it will be looped only once and you don't to filter for every single item
I am making a video app like tiktok / instagram reel and i have a flatlist as below
All my videos play automatically and i have it set so that its paused on render (at the moment), I am tying to play a video when it is visible on the screen and pause the other vodeos, but it doesn't work i can't seem to see anything online on how i can pause the other videos or possibly just render one video until i scroll but all videos are set to true no matter what i do.
how can i get the video that is visible to play and then pause when user scrolls and then play the other visible video?
I have been at this for 2 days and my head is Fried, any help would be appreciated :(
PostScreen.js
const [state, setState] = useState({
isVisible: false,
})
const videoData [
{
id: 1,
video: videourl
},
{
id: 2,
video: videourl
},
];
const _onViewableItemsChanged = useCallback(({ viewableItems }) => {
if(viewableItems[0]){
if(viewableItems[0].isViewable){
setState({...state, isVisible: true})
}
}
}, []);
const _viewabilityConfig = {
itemVisiblePercentThreshold: 50
}
<FlatList
data={videosData}
decelerationRate={'fast'}
showsVerticalScrollIndicator={false}
snapToInterval={Dimensions.get('window').height}
snapToAlignment={"start"}
initialScrollIndex={0}
disableIntervalMomentum
onViewableItemsChanged={_onViewableItemsChanged}
viewabilityConfig={_viewabilityConfig}
renderItem={ ({ item }) => (
<View>
<VideoPlayerComponent data={item} />
</View>
)}
/>
VideoPlayerComponent
const [data] = useState(props.data)
const [paused, setPaused] = useState(true);
return(
<View>
<TouchableWithoutFeedback
onPress={() => setPaused(!paused)}
>
<View>
<Video
style={styles.fullScreen}
source={data.video}
resizeMode="cover"
paused={paused}
repeat
/>
{
paused ? (
<View style={styles.pausedIcon}>
<Icon name="play" type="ionicon" color="white" size={68} />
</View>
): null
}
</View>
</TouchableWithoutFeedback>
</View>
)
friends I have solved the issue for my react native video project.
the issue was that all videos are playing in Flatlist but we need to play only singal video on the current viewport and pause the rest.
just do the following steps to solve all videos playing issue
1: npm install #svanboxel/visibility-sensor-react-native
2: import VisibilitySensor from '#svanboxel/visibility-sensor-react-native'
3: do this
import VisibilitySensor from '#svanboxel/visibility-sensor-react-native'
const video = ()=>{
const [paused, setpaused] = useState(true)
return(
<VisibilitySensor onChange={(isVisible)=>{
return(
console.log(isVisible),
isVisible?setpaused(false):setpaused(true)
)
}
}
>
<View>
<Video
source={{uri: 'https://d8vywknz0hvjw.cloudfront.net/fitenium-media-prod/videos/45fee890-a74f-11ea-8725-311975ea9616/proccessed_720.mp4'}}
style={styles.video}
onError={(e) => console.log(e)}
resizeMode={'cover'}
repeat={true}
paused={paused}
/>
</View>
</VisibilitySensor>
)
}
4: I have just given you the basic structure you can add styling stuff as your requirements.
5: remember that always add your view/video elements between the VisibilitySensor tags, otherwise it will not work.
6: this code will give you true when your video component will render in flatlist viewport and remainig will give you false. with this you can manage play/pause state of video element.
thanks...
I managed to use the inviewport library
using this snippiti managed to convert to functional class
in my functional class i just passed a flatlist as it was.
<FlatList
data={videos}
decelerationRate={'fast'}
showsVerticalScrollIndicator={false}
snapToInterval={Dimensions.get('window').height}
snapToAlignment={"start"}
initialScrollIndex={0}
disableIntervalMomentum
renderItem={ ({ item }) => (
<View>
<VideoPlayerComponent data={item}/>
</View>
)}
/>
then in my VideoPlayerComponent i do this
const video = useRef(ref)
const playVideo = () => {
if(video) {
setPaused(false);
}
}
const pauseVideo = () => {
if(video) {
setPaused(true);
}
}
const handlePlaying = (isVisible) => {
isVisible ? playVideo() : pauseVideo();
}
return (
<View>
<Video
ref={ ref => {video.current = ref}}
style={styles.fullScreen}
source={data.video}
paused={paused}
resizeMode="cover"
repeat
/>
</View>
)
This will play the video that is in. view and will pause the other based on the ref passed to it.
Hope this helps anyone stuck as i was stuck for a few days :)
I'm fetching data from an API and implementing search in a FlatList but the keyboard dismisses automatically after every key-press.
I'm refering this article but implementing it in a Functional Component.
const renderHeader = () => {
return <SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => searchFilterFunction(text)}
value={value}
autoCorrect={false} />;
}
const searchFilterFunction = (text) => {
setValue(text);
const newData = APIData.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.includes(textData);
});
setData(newData);
}
return (
<FlatList
keyExtractor={(item) => item._id}
data={data}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
onRefresh={handleRefresh}
refreshing={refreshing}
renderItem={({ item }) => (
<Card>
<Card.Content style={{ flexDirection: "row" }}>
<Text>{"Name: " + item.name}</Text>
<Text>{"Status: " + (item.isaccepted ? "Accepted" : "Pending")}</Text>
<Text>{"ID: " + item.id}</Text>
</Card.Content>
</Card>
)} />
)
Thanks in advance.
I was doing same thing, adding search bar as a header to FlatList. Unfortunately, this also updates the header when you update the flatlist data when search filtering is complete and hence focusing out of SearchBar. At the end, due to time constraints, I ended up putting SearchBar at the top of FlatList.
Try rendering your ListHeaderComponent as JSX element directly, instead of using callback
<FlatList
ListHeaderComponent={
<View>
<Text>I am the header</Text>
</View>
}
...props
/>