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>
);
}
}
Related
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
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.
Is there a way to control the word-break behaviour for text within a Text component? With a multiple-line Text component on iOS it automatically breaks the text where there are word breaks (I haven't checked android yet, but I'd need it to work there too). I was hoping to change that behaviour to break on the per-character level, like you can do this CSS rule:
word-break: break-all;
transform <text>str</text> to <View><text>s</text><text>t</text><text>r</text></View> by using split & map!
export default class PageCard extends React.Component {
constructor(props) {
super(props);
this.state = {
tail: false,
longWord: 'longWord longWord bbbbbbbbbbbbbbbbbbbbb longWord longWord longWord',
longWordTail: ' longWord longWord longWord bbbbbbbbbbbbbbbbbbbbb longWord longWord longWord',
longWordTail2: ' longWord longWord bbbbbbbbbbbbbbbbbbbbb longWord longWord longWord',
};
}
render() {
return (
<ScrollView style={{flex: 1, paddingTop: 60, paddingHorizontal: 10}}>
<View style={Styles.breakWordWrap}>
<Text numberOfLines={2} ellipsizeMode="tail" style={Styles.breakWord}>
{this.state.longWord}
</Text>
</View>
<View style={Styles.breakWordWrap}>
{
this.breakWord(this.state.longWord)
}
</View>
<View style={[Styles.breakWordWrap, Styles.breakWordWrapTail]}>
{
this.breakWord(this.state.longWordTail)
}
</View>
<View style={[Styles.breakWordWrap, Styles.breakWordWrapTail]}>
{
this.breakWord(this.state.longWordTail, true)
}
</View>
{/*<View style={[Styles.breakWordWrap, Styles.breakWordWrapTail]}>*/}
{/* {*/}
{/* this.breakWord(this.state.longWordTail2, true)*/}
{/* }*/}
{/*</View>*/}
</ScrollView>
)
}
breakWord = (str = '', tail = false) => {
let strArr = (tail ? str + ' ' : str).split('');
return strArr.map((item, index) => tail && strArr.length === index + 1 ?
<Text key={item + index} style={[Styles.breakWord, Styles.breakWordTail, !this.state.tail && Styles.breakWordTailHide]}>...</Text> :
(tail && strArr.length === index + 2 ? <Text key={item + index} style={[Styles.breakWord]} onLayout={this.breakWordLast}>{item}</Text>
: <Text key={item + index} style={[Styles.breakWord]}>{item}</Text>)
);
}
breakWordLast = (e) => {
console.log(e.nativeEvent.layout)
if (e.nativeEvent.layout.y > 50) {
this.setState({
tail: true
})
}
}
}
const Styles = {
box: {
marginTop: 10,
},
title: {
fontWeight: 'bold',
color: '#333',
textAlign: 'center'
},
breakWordWrap: {
flexWrap: 'wrap',
flexDirection: 'row',
// not
borderWidth: 1,
marginTop: 30,
},
breakWordWrapTail: {
position: 'relative',
height: 50,
overflow: 'hidden'
},
breakWord: {
lineHeight: 25
},
breakWordTail: {
position: 'absolute',
backgroundColor: '#fff',
right: 0,
bottom: 0,
height: 25
},
breakWordTailHide: {
opacity: 0
}
}
There is an Android-only textbreakstrategy property for Text components that allows some control on how a text should be split. The property values map to Android's native android:breakStrategy values.
This property does not seem to translate to iOS though.
Otherwise, if you have preknowledge about the Text value, you could indicate text split with a Soft Hyphen
<Text>aaaaaaaaaaaaaaaaaa bbbbbbbbbbbCCCCC.</Text>
This would render as:
aaaaaaaaaaaaaaaaaa bbbbbbbbbbb-
CCCCC.
If neither suffice, I suppose this is beyond the capabilities of a Text component and you may have to revert to another component. Maybe one that renders simple html.
Try add flexWrap: "wrap" to the style of the Text component
Example:
Code
<View style={styles.container}>
<Text style={styles.text}>{text}</Text>
</View>
Style
export default StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
text: {
width: 0,
flexGrow: 1,
flex: 1,
}
});
There is the possibility with following code
<Text style={{ flexShrink: 1 }}>
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
I am using react-navigation .
I want to add icon for the tab.
CustomTabs.js from example directory
if you are to use react-native-vector-icon is much easier, just create an array like the one i created below, for all the names of the icon you want to use and if you want to use image, then you will have to use image links because the last time i checked react native won't allow you to load static assets dynamically.
Benefit of using an icon especially react-native-vector-icon:
Access to tonnes of iconsets.
Styling based on if its focused or not.
....and others things i can't remember.
`
.....
import Icon from 'react-native-vector-icons/Ionicons';
const styles = {
body: {
backgroundColor: '#3b4147',
height: 60,
},
tabWrapper: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
height: 50,
},
tabInnerWrapper: {
marginRight: 12,
marginLeft: 12,
justifyContent: 'center',
alignItems: 'center',
},
textStyle: {
fontSize: 12,
color: '#62666b',
},
focusTextStyle: {
fontSize: 12,
color: '#acafb1',
},
};
const {body, tabWrapper, tabInnerWrapper, textStyle, focusTextStyle} = styles;
const focusIconColor = '#acafb1';
const iconColor = '#62666b';
const IconNames = ['ios-compass-outline', 'ios-cut-outline', 'ios-chatboxes-outline'];
const IconNamesFocus = ['ios-compass', 'ios-cut', 'ios-chatboxes'];
const CustomTabBar = ({ navigation: { state, navigate }}) => {
const { routes } = state;
return (
<View style={body}>
<View style={tabWrapper}>
{routes && routes.map((route, index) => {
const focused = index === state.index;
return (
<TouchableOpacity
key={route.key}
onPress={() => navigate(route.routeName)}
style={tabInnerWrapper}
>
<Icon
name={focused ? IconNamesFocus[index] : IconNames[index]}
size={25}
color={focused ? focusIconColor : iconColor}
/>
<Text style={focused ? focusTextStyle : textStyle}>
{route.routeName}
</Text>
</TouchableOpacity>
);
})}
</View>
</View>
);
};
`