I try to build a news app that shows on the MainPage an overview of news items.
The first 3 items need to be rendered different as the rest, using a FlatList.
First item is a 100% background image with some text on it (did this with: if index === 0))
The second and third item needs to be background images with titles in a row (so next to each other)
The rests is a list with image, title, and date (underneath each other)
I tried everything but item 2 and 3 is not working.
Tried with this little basic test:
import React, { Component } from "react";
import { View, StyleSheet, Text, FlatList } from "react-native";
export default class Screen1 extends Component {
state = {
data: [
{
text: "one"
},
{
item1: {
text: "two"
},
item2: {
text: "three"
}
},
{
item1: {
text: "four"
},
item2: {
text: "five"
}
},
{
item1: {
text: "six"
}
}
]
};
renderItem = ({ item, index }) => {
if (index === 0) {
return (
<View style={styles.bigSquare}>
<Text> {item.text} </Text>{" "}
</View>
);
} else if (index > 0 || index <= 3) {
return (
<View
style={{
flexDirection: "row"
}}
>
{" "}
{item.item2 && (
<View
style={[
styles.smallSquare,
{
backgroundColor: "red"
}
]}
>
<Text> {item.item2.text} </Text> <Text> {item.item2.text} </Text>{" "}
</View>
)}{" "}
</View>
);
}
};
keyExtractor = (item, index) => `${index}`;
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.data}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>{" "}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
bigSquare: {
flexDirection: "column",
height: 220,
width: "100%",
margin: 10,
backgroundColor: "yellow",
justifyContent: "center",
alignItems: "center"
},
smallSquare: {
height: 100,
width: 100,
margin: 10,
backgroundColor: "green",
justifyContent: "center",
alignItems: "center"
}
});
Can someone point me in the right direction?
Example:
This approach is a little bit different. Separate your list into 3 parts which include,
First item
Second & third items
Rest of items (use FlatList to render this part)
Finally, you can display those 3 parts in different ways. But make sure to display part 1 & 2 as a ListHeaderComponent of FlatList.
import React, { Component } from "react";
import { SafeAreaView, View, FlatList, StyleSheet, Text, Dimensions } from "react-native";
const ScreenWidth = Dimensions.get('window').width;
const DATA = [
{
id: "1",
title: "First Item"
},
{
id: "2",
title: "Second Item"
},
{
id: "3",
title: "Third Item"
},
{
id: "4",
title: "Forth Item"
},
{
id: "5",
title: "Fifth Item"
},
{
id: "6",
title: "Sixth Item"
},
{
id: "7",
title: "Seventh Item"
}
];
export default class Example extends Component {
renderHeader = () => (
<View>
{/* Display index === 0 item */}
<View style={styles.bigSquare}>
<Text>{DATA[0].title}</Text>
</View>
{/* Display index > 0 && index < 3 items */}
<View style={{ flexDirection: 'row' }}>
<View style={styles.middleSqure}>
<Text>{DATA[1].title}</Text>
</View>
<View style={styles.middleSqure}>
<Text>{DATA[2].title}</Text>
</View>
</View>
</View>
)
renderItems = ({ item }) => (
<View style={styles.smallSquare}>
<Text>{item.title}</Text>
</View>
);
render() {
return (
<SafeAreaView style={{flex: 1, marginTop: 20}}>
{/* Display rest of item in a FlatList */}
<FlatList
data={DATA.slice(2)}
renderItem={this.renderItems}
ListHeaderComponent={this.renderHeader}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
bigSquare: {
height: 220,
width: ScreenWidth - 20,
margin: 10,
backgroundColor: "yellow",
justifyContent: "center",
alignItems: "center"
},
middleSqure: {
height: (ScreenWidth - 40) / 2,
width: (ScreenWidth - 40) / 2,
margin: 10,
backgroundColor: "red",
justifyContent: "center",
alignItems: "center"
},
smallSquare: {
height: 100,
width: 100,
margin: 10,
backgroundColor: "green",
justifyContent: "center",
alignItems: "center"
},
});
Hope this helps you. Feel free for doubts.
Related
I'm creating onBoarding screens and I have created each of the screen item as a component and trying to render them with Flatlist so far everything is working smoothly but when I swipe the flatlist to see the other screens it's not working it swipes 40% and and forcefully shows the current screen it seems like there is some flex styling issues and I could able to figure it out please suggest.
Here is the video for explaination: https://youtube.com/shorts/pHbTs7ifMww
OnBoardingScreen.js
import React, { useState } from 'react';
import { Dimensions, FlatList, SafeAreaView, View, StyleSheet, Text, Image } from 'react-native';
const { width, height } = Dimensions.get('window');
const COLORS = {primary : '#ff006c', white: '#ffffff', black: '#000000'};
const slides = [
{
id: '1',
image: require('../../images/OnBoardingImages/1.png'),
title: 'You can mark time',
description: 'Lorem ipsum is a placeholder text commonly used to demonstrate the visual',
},
{
id: '2',
image: require('../../images/OnBoardingImages/2.png'),
title: 'You can mark time',
description: 'Lorem ipsum is a placeholder text commonly used to demonstrate the visual',
},
{
id: '3',
image: require('../../images/OnBoardingImages/3.png'),
title: 'You can mark time',
description: 'Lorem ipsum is a placeholder text commonly used to demonstrate the visual',
},
]
const Slide = ({item}) => {
return (
<View style={styles.slideView}>
<Image source={item.image} style={styles.slideImage} />
<Text style={styles.slideTitle}>{item.title}</Text>
<Text style={styles.slideDescription}>{item.description}</Text>
</View>
);
}
const OnBoardingScreen = ({ navigation }) => {
const [currentSlideIndex, setCurrentSlideIndex] = useState(0);
const Footer = () => {
return (
<View style={styles.footer}>
<View style={styles.pagination}>
{slides.map((_, index) => (<View key={index} style={[styles.paginationItem, currentSlideIndex == index && {backgroundColor: 'grey'} ]} /> ))}
</View>
</View>
);
};
return (
<SafeAreaView style={styles.root}>
<FlatList
data={slides}
contentContainerStyle={{flex: 1}}
showsHorizontalScrollIndicator={false}
horizontal
pagingEnabled
renderItem={({item}) => <Slide item={item} />} />
<Footer />
</SafeAreaView>
);
};
export default OnBoardingScreen;
const styles = StyleSheet.create({
root: {
flex: 1,
backgroundColor: COLORS.white,
},
slideView: {
alignItems: 'center',
},
slideImage: {
height: '75%',
width: width,
resizeMode: 'contain',
},
slideTitle: {
color: COLORS.primary,
fontSize: '22',
fontWeight: 'bold',
marginVertical: 10,
paddingHorizontal: 10,
textAlign: 'center',
},
slideDescription: {
fontSize: 18,
paddingHorizontal: 20,
color: 'grey',
textAlign: 'center',
},
footer: {
height: height * 0.25,
paddingHorizontal: 20,
justifyContent: 'space-between',
},
pagination: {
flexDirection: 'row',
justifyContent: 'center',
marginTop: 20,
},
paginationItem: {
height: 10,
width: 10,
backgroundColor: COLORS.primary,
marginHorizontal: 3,
borderRadius: 50,
},
});
App.js
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import Navigation from './src/navigation';
export default function App() {
return (
<View style={styles.container}>
<Navigation />
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
Update this style
slideView: {
alignItems: 'center',
width: width
},
I'm trying to make animation in flatlist, something like this, it will have spring animation when moving item
Something like apple music you can see: https://streamable.com/yg1j2j
With the help of #David Scholz, I was able to make exactly the same, here is expo for that:
https://snack.expo.dev/#quockhanh210199/animated-list-selection
But problem is, when i enable scroll it will have weird animation, so my question is, how to make the animation behavior correct if i enable scroll, you can see the weird animation if you enable scroll and and more item, or use below code:
import React, {useState, useCallback} from 'react';
import { StyleSheet, Text,View, SafeAreaView, ScrollView, StatusBar, Animated,FlatList,TouchableOpacity } from 'react-native';
const App = () => {
const data = [
{
id: "1",
text: "Results",
},
{
id: "2",
text: "Products",
},
{
id: "3",
text: "Stores",
},
{
id: "4",
text: "Stores",
},
{
id: "5",
text: "Stores",
},
{
id: "6",
text: "Stores",
},
]
const [translateValue] = useState(new Animated.Value(0))
const [selected, setSelected] = useState(0)
const onPress = React.useCallback(
(index) => {
setSelected(index)
Animated.spring(translateValue, {
toValue: index * 100,
velocity: 5,
useNativeDriver: true,
}).start()
},
[translateValue, setSelected]
)
return (
<View style={{ flexDirection: "row", marginTop: 100 }}>
<Animated.View
style={[
{
position: "absolute",
backgroundColor: "black",
top: -5,
right: 0,
bottom: 0,
left: 15,
width: 70,
height: 30,
borderRadius: 12,
transform: [{ translateX: translateValue }],
},
]}
/>
<FlatList
data={data}
horizontal={true}
scrollEnabled={true}
renderItem={({ item, index }) => {
return (
<TouchableOpacity onPress={() => onPress(index)}>
<View style={{ width: 100, borderRadius: 10 }}>
<Text style={[{ textAlign: "center" }, index === selected ? { color: "white" } : { color: "black" }]}>
{item.text}
</Text>
</View>
</TouchableOpacity>
)
}}
keyExtractor={(item) => item.id}
/>
</View>
)
}
export default App
Please help, thank you so much
When I submit a form in React Native I get the below error undefined is not an object (evaluating 'title.length'). This problem occurs when I submit a form when the Card.js should be rendering the data from the form. I have checked and its getting the data back fine, seems to be a problem with rendering the data that its reading as undefined. After this error the form actually submits successfully and the Card.js displays the data.
Card.js
import React from "react";
import {
StyleSheet,
View,
Text,
ImageBackground,
TouchableOpacity,
} from "react-native";
const Card = (props) => {
const {
navigation,
title,
address,
homeType,
description,
image,
yearBuilt,
price,
id,
} = props;
return (
<TouchableOpacity
onPress={() => props.navigation.navigate("HomeDetail", { houseId: id })}
>
<View style={styles.card}>
<View style={styles.titleContainer}>
<Text style={styles.title}>
{title.length > 30 ? title.slice(0, 30) + "..." : title}
</Text>
</View>
<View style={styles.imageContainer}>
<ImageBackground source={{ uri: image }} style={styles.image}>
<Text style={styles.price}>${price}</Text>
<View style={styles.year}>
<Text style={styles.yearText}>{yearBuilt}</Text>
</View>
</ImageBackground>
</View>
<View style={styles.description}>
<Text style={styles.descriptionText}>
{description.length > 100
? description.slice(0, 100) + "..."
: description}
</Text>
</View>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
card: {
shadowColor: "black",
shadowOpacity: 0.25,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 8,
borderRadius: 10,
backgroundColor: "#ffffff",
elevation: 5,
height: 300,
margin: 10,
},
titleContainer: {
height: "15%",
padding: 10,
},
title: {
fontSize: 18,
fontWeight: "bold",
color: "gray",
},
imageContainer: {
width: "100%",
height: "65%",
overflow: "hidden",
},
image: {
width: "100%",
height: "100%",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-end",
},
price: {
fontSize: 30,
color: "#fff",
margin: 10,
},
year: {
margin: 10,
backgroundColor: "#2652B0",
height: 25,
width: 80,
borderRadius: 5,
},
yearText: {
fontSize: 20,
color: "#fff",
textAlign: "center",
},
description: {
margin: 10,
},
descriptionText: {
fontSize: 16,
color: "gray",
},
});
export default Card;
HomeListScreen.js
import React, { useEffect, useState } from "react";
import {
StyleSheet,
View,
Text,
FlatList,
ActivityIndicator,
} from "react-native";
import { FloatingAction } from "react-native-floating-action";
import { useDispatch, useSelector } from "react-redux";
import Card from "../components/Card";
import * as houseAction from "../redux/actions/houseAction";
const HomeListScreen = (props) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const { houses } = useSelector((state) => state.house);
useEffect(() => {
setIsLoading(true);
dispatch(houseAction.fetchHouses())
.then(() => setIsLoading(false))
.catch(() => setIsLoading(false));
}, [dispatch]);
if (isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" />
</View>
);
}
if (houses.length === 0 && !isLoading) {
return (
<View style={styles.centered}>
<Text>No home found. You could add one!</Text>
</View>
);
}
return (
<View style={styles.container}>
<FlatList
data={houses}
keyExtractor={(item) => item._id}
renderItem={({ item }) => (
<Card
navigation={props.navigation}
title={item.title}
address={item.address}
homeType={item.homeType}
description={item.description}
price={item.price}
image={item.image}
yearBuilt={item.yearBuilt}
id={item._id}
/>
)}
/>
<FloatingAction
position="right"
animated={false}
showBackground={false}
onPressMain={() => props.navigation.navigate("AddHome")}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
export default HomeListScreen;
<Text style={styles.title}>
{ title ? (title.length > 30 ? title.slice(0, 30) + "..." : title):true}
</Text>
Make sure that title is not undefined.
Below is the code component for the customer picker
import React, { useEffect, useState } from "react";
import { connect } from 'react-redux';
import {
TouchableOpacity,
FlatList,
SafeAreaView,
StatusBar,
StyleSheet,
Text,
View,
Button,
Alert,
} from "react-native";
import { screenHeight, screenWidth } from "../constants";
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "Client Not Found"
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Client refused"
},
];
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const StatusOptions = (props) => {
const [selectedId, setSelectedId] = useState(null);
const renderSeparator = () => (
<View
style={{
backgroundColor: "grey",
height: 0.8
}}
/>
);
const ListHeader = () => {
//View to set in Header
return (
<View style={{ height: 20 }}></View>
);
};
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6cd9ff" : "white";
return (
<Item
item={item}
onPress={() => {
setSelectedId(item.id);
console.log("SELECTED ID _ STATUSOPTIONS component : ", selectedId);
const val = DATA.filter(status => status.id == selectedId).map(filteredStatus => filteredStatus.title);
console.log("VALLLLLLLLLLLLLLLLLLLLLLLUUUUUUEEEEEEEEEEEEEEEEEEEE:::: ", val);
props.navigation.navigate('AnotherScreen');
}}
style={{ backgroundColor }}
/>
);
};
return (
<View style={{ bottom: 0, flex: 1, position: 'absolute', width: screenWidth, }}>
<View style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={ListHeader}
style={{
backgroundColor: "white",
width: "100%",
borderTopRightRadius: 20,
borderTopLeftRadius: 20,
zIndex: 1,
}}
/>
</View>
<View style={{ backgroundColor: "grey", height: 0.4 }} />
<View style={styles.closeButtonContainer}>
<TouchableOpacity style={styles.closeButton}
onPress={() => {
props.setStatusOptionsVisible(false)
}}>
<Text style={styles.title}>Close</Text>
</TouchableOpacity>
</View>
</View>
);
};
function mapStateToProps(state) {
return {
StatusOptionsVisible: state.volunteerItems.statusOptionsVisible,
currentTaskItemId: state.taskItems.taskItemId,
};
}
function mapDispatchToProps(dispatch) {
return {
setStatusOptionsVisible: (visible) => dispatch({ type: 'SET_STATUS_VISIBLE', statusVisibility: visible }),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(StatusOptions);
const styles = StyleSheet.create({
closeButton: {
backgroundColor: 'lightgrey',
borderRadius: 10,
height: 50,
justifyContent: 'center',
width: '90%',
},
closeButtonContainer: {
alignItems: 'center',
height: 90,
justifyContent: 'center',
backgroundColor: 'white',
},
textStyle: {
textAlign: "center",
},
container: {
borderRadius: 30,
flex: 4,
width: screenWidth,
},
item: {
padding: 20
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: "black",
textAlign: "center"
}
});
the console Log : ("SELECTED ID _ STATUSOPTIONS component : ", selectedId)
in render Item function returns null for first picker item selection and the returns the previous value for the next picker item selection , can anyone please help with fixing it ?
Try to use this
useEffect(() => {
console.log("SELECTED ID _ STATUSOPTIONS component : ", selectedId);
if(selectedId != null) {
const val = DATA.filter(status => status.id == selectedId).map(filteredStatus => filteredStatus.title);
console.log("VALLUUEEEEEEEEEEEEEEEEEEEE:::: ", val);
}
}, [selectedId])
I have a list of text elements that I want to underline when clicked. If I add the text decoration to the tabText then obviously it is applied to all items. How can I make sure that the when I click on another tab, the underline from the previous tab gets removed?
Is there any way I can add or remove items from the style element upon clicking on an item?
//onPress={() => {}}>
const tabs = [
{
id: 1,
text: 'Alle List',
},
{
id: 2,
text: 'Second',
},
];
export const Tab: React.FunctionComponent = () => {
return (
<View style={styles.tabView}>
{tabs.map((item: any) => (
<View>
<Text style={styles.tabText}>{item.text}</Text>
</View>
))}
</View>
);
};
const styles = StyleSheet.create({
tabView: {
paddingTop: moderateScale(15),
paddingLeft: moderateScale(20),
paddingRight: moderateScale(20),
flexDirection: 'row',
justifyContent: 'space-between',
},
tabText: {
color: 'white',
paddingBottom: moderateScale(10),
//textDecorationLine: 'underline'
},
});
Codesandbox (with tabText items as an array too):
https://snack.expo.io/#nhammad/shallow-watermelon
You can use useState to save the selected index and then apply another style based on the selected index. Here is a quick modification to your script and the snack link is at the end.
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
const tabs = [
{
id: 1,
text: 'All Friends',
},
{
id: 2,
text: 'Second',
},
{
id: 3,
text: 'Third',
},
];
export default function App() {
const [selected, setSelected] = React.useState(null)
return (
<View style={styles.container}>
<View style={styles.tabView}>
{tabs.map((item: any, index) => (
<TouchableOpacity onPress={()=>setSelected(index)}>
<View>
<Text style={[styles.tabText,selected===index?styles.selected:null]}>{item.text}</Text>
</View>
</TouchableOpacity>
))}
</View>
</View>
);
}
const styles = StyleSheet.create({
tabView: {
paddingTop: 15,
paddingLeft: 20,
paddingRight: 20,
flexDirection: 'row',
justifyContent: 'space-between',
},
tabText: {
color: 'black',
paddingBottom: 10,
},
selected: {
textDecorationLine: 'underline'
},
});
https://snack.expo.io/#saachitech/da6280
You can add different styles for your tab depending on active route
<Text style={[(this.props.activeRouteName == route.routeName) ? styles.tabActiveText: styles.tabText]}>{item.text}</Text>
And then in the styles
tabActiveText: {
color: 'white',
paddingBottom: moderateScale(10),
textDecorationLine: 'underline'
}