I am trying to add a different image to each key of my flatlist. However, the images aren't being showed on the screen by rendering the items, just the keys. The source of the images is correct, so as the name of the images. Can you find the mistake that I have made?
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
};
}
render() {
//Data can be coming from props or any other source as well
const data = [
{
key: 'Athlean-X',
logo: 'athlean_x.png'
},
{
key: 'Panelaria Ricardo Faria',
logo: "euro2020.png"
},
{
key: 'tEsLa',
logo: "jamor.png"
},
{
key: 'Lols Inc.',
logo: "rock_in_rio.png"
},
{
key: 'Vale de Silicone',
logo: 'splash.png'
},
{
key: 'Empresa do Helder',
logo: "edp_cooljazz.png"
},
{
key: 'BD Swiss',
logo: "superbock_superrock.png"
}
];
const filteredData = this.state.searchText
? data.filter(x =>
x.key.toLowerCase().includes(this.state.searchText.toLowerCase())
)
: data;
return (
<View style={styles.container}>
<View style={styles.flatlist}>
<FlatList
data={filteredData}
renderItem={({ item }) => (
<View style={{flexDirection:'row'}}>
<Image
style={{width:50, height:50, alignSelf: 'center', borderRadius:300}}
source={{uri:item.logo}}
/>
<Text style={styles.item}>{item.key}</Text>
</View>
)}
/>
</View>
</View>
);
}
}
The images saved locally must be called using require function.
<FlatList
data={filteredData}
renderItem={({ item }) => (
<View style={{flexDirection:'row'}}>
<Image
style={{width:50, height:50, alignSelf: 'center', borderRadius:300}}
source={require(item.logo)}
/>
<Text style={styles.item}>{item.key}</Text>
</View>
)}
keyExtractor={item=>item.key}
/>
Flatlist requires every individual component in the list to have a unique key. That's why item.key is provided to key extractor.
Related
When I am trying to pass data from Login screen to MyProfile screen then I got undefined value.
I am confused that why I am getting undefined value ?
Here is code of my main navigation files.
route.js
import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer, getFocusedRouteNameFromRoute } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { createDrawerNavigator } from "#react-navigation/drawer";
import LoginScreen from "./../screens/Login/index.js";
import MyProfileScreen from "../screens/MyProfile/index.js";
import AboutUsScreen from "../screens/AboutUs/index.js";
import SettingScreen from "../screens/Setting/index.js";
import { Image, StyleSheet, View, TouchableOpacity } from "react-native";
import { heightPercentageToDP as hp, widthPercentageToDP as wp } from 'react-native-responsive-screen';
import { RFValue } from "react-native-responsive-fontsize"
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
const NavigationDrawerStructure = (props) => {
const toggleDrawer = () => {
props.navigationProps.toggleDrawer();
}
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity onPress={() => toggleDrawer()}>
<Image
source={{ uri: 'https://raw.githubusercontent.com/AboutReact/sampleresource/master/drawerWhite.png' }}
style={{
width: 25,
height: 25,
marginLeft: 5
}}
/>
</TouchableOpacity>
</View>
)
}
const geHeaderTitle = (route) => {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'MyProfileScreen';
switch (routeName) {
case 'MyProfileScreen':
return 'Profile';
case 'AboutUsScreen':
return 'AboutUs';
case 'SettingScreen':
return 'Setting';
}
}
const BottomTab = () => {
return (
<Tab.Navigator initialRouteName="MyProfileScreen"
tabBarOptions={{
activeTintColor: "red",
labelStyle: {
fontSize: RFValue('14'),
marginTop: 5
},
style: { height: hp('11') }
}}
>
<Tab.Screen
name="MayProfileScreen"
component={MyProfileScreen}
options={{
tabBarLabel: 'Profile',
tabBarIcon: ({ focused }) => (
focused ?
<Image source={require('./../../asstes/images/profile.png')} style={styles.activeImg} /> :
<Image source={require('./../../asstes/images/profile.png')} style={styles.deActiveImg} />
)
}}
/>
<Tab.Screen
name="AboutUsScreen"
component={AboutUsScreen}
options={{
tabBarLabel: 'AboutUs',
tabBarIcon: ({ focused }) => (
focused ?
<Image source={require('./../../asstes/images/aboutus.png')} style={styles.activeImg} /> :
<Image source={require('./../../asstes/images/aboutus.png')} style={styles.deActiveImg} />
)
}}
/>
<Tab.Screen
name="SettingScreen"
component={SettingScreen}
options={{
tabBarLabel: 'Setting',
tabBarIcon: ({ focused }) => (
focused ?
<Image source={require('./../../asstes/images/setting.png')} style={styles.activeImg} /> :
<Image source={require('./../../asstes/images/setting.png')} style={styles.deActiveImg} />
)
}}
/>
</Tab.Navigator>
)
}
const HomeStack = ({ navigation }) => {
return (
<Stack.Navigator initialRouteName="LoginScreen">
<Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
<Stack.Screen name="MyProfileScreen" component={BottomTab}
options={({ route }) => ({
headerTitle: geHeaderTitle(route),
headerLeft: () => (
<NavigationDrawerStructure navigationProps={navigation} />
),
title: 'Profile',
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
})}
/>
<Stack.Screen name="AboutUsScreen" component={AboutUsScreen}
options={{
title: 'AboutUS',
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}} />
<Stack.Screen name="SettingScreen" component={SettingScreen}
options={{
title: 'Setting',
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}}
/>
</Stack.Navigator>
)
}
const AboutUsStack = ({ navigation }) => {
return (
<Stack.Navigator initialRouteName="AboutUsScreen"
screenOptions={{
headerLeft: () => (
<NavigationDrawerStructure navigationProps={navigation} />
),
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}}
>
<Stack.Screen name="AboutUsScreen" component={AboutUsScreen} options={{ title: 'AboutUs' }} />
</Stack.Navigator>
)
}
const SettingStack = ({ navigation }) => {
return (
<Stack.Navigator initialRouteName="SettingScreen"
screenOptions={{
headerLeft: () => (
<NavigationDrawerStructure navigationProps={navigation} />
),
headerStyle: { backgroundColor: '#f4511e' },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold' }
}}
>
<Stack.Screen name="SettingScreen" component={SettingScreen} options={{ title: 'Setting', }} />
</Stack.Navigator>
)
}
const Navigation = () => {
return (
<NavigationContainer>
<Drawer.Navigator
drawerContentOptions={{
activeTintColor: '#e91e63',
itemStyle: { marginVertical: 5 }
}}
>
<Drawer.Screen name="HomeStack" options={{ drawerLabel: 'Profile' }} component={HomeStack} />
<Drawer.Screen name="AboutUsStack" component={AboutUsStack} options={{ drawerLabel: 'AboutUs' }} />
<Drawer.Screen name="SettingStack" component={SettingStack} options={{ drawerLabel: 'Setting' }} />
</Drawer.Navigator>
</NavigationContainer>
)
}
const styles = StyleSheet.create({
activeImg: {
height: hp('4.8'), width: wp('8.5'), marginTop: 10, borderRadius: 12, tintColor: 'red'
},
deActiveImg: {
height: hp('4.8'), width: wp('8.5'), marginTop: 10, borderRadius: 12, tintColor: 'gray'
}
})
export default Navigation;
I am calling below function when user click on Login button.
Here is some lines of code that how I am trying to pass data from Login screen to MyProfile screen
Login screen
const resetTextInput = () => {
setName(null);
setPassword(null);
navigation.navigate('MyProfileScreen', { userName: name, userPwd: password,});
}
<TouchableOpacity style={styles.loginBtn} onPress={() => { resetTextInput() }}>
<Text style={styles.loginBtnTxt}>Login</Text>
</TouchableOpacity>
Here is some lines of code that how I am trying to get data from Login screen to MyProfile screen.
MyProfle screen
useEffect(() => {
console.log("username is-->",JSON.stringify(route?.params?.userName));
console.log("userpassword is-->",JSON.stringify(route?.params?.userPwd));
});
Perhaps pass route.params? as an argument inside your useEffect?
useEffect(() => {
...
},[route.params?]);
That way it'll apply the effect when you navigate into it.
Do you have a multiple stack navigator? If you are at different stack navigator and you want to pass params to a screen from different stack navigator, you have to specify the screen.
For example you are at the AuthStack and you want to navigate to MyProfileScreen from the ProfileStack:
navigation.navigate('ProfileStack', { screen: 'MyProfileScreen', params: { //data here } });
if not, I think you got a typo in your BottomTab component. Change the name of the first tab from "MayProfileScreen" to "MyProfileScreen".
then use this to navigate:
navigation.navigate('MyProfileScreen', { //params here });
After some searching online, I have found some decent code to base my requirements on. I have successfully retrieved data from my web API and display the content on a page view a FlatList. However, the image URL retrieved from the API is not being processed and I am not sure why.
This is my results
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
Image
} from 'react-native';
//import data from './json/FetchJsonDataExample.json';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
isLoading: true, // check if json data (online) is fetching
dataSource: [], // store an object of json data
};
}
componentDidMount() {
return fetch("https://www.apakataorang.com/wp-json/wp/v2/posts?per_page=20")
.then((response) => response.json())
.then((responseJson) => {
// set state value
this.setState({
isLoading: false, // already loading
dataSource: responseJson
});
})
.catch((error) => {
ToastAndroid.show(error.toString(), ToastAndroid.SHORT);
});
}
render() {
// show waiting screen when json data is fetching
if(this.state.isLoading) {
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
return(
<View style={{flex: 1, paddingTop:30}}>
<FlatList
data={this.state.dataSource}
renderItem={({item}) => {
return (
<View>
<Text style={styles.info}>{item.title.rendered}</Text>
<Image
source={{uri: 'item.jetpack_featured_media_url'}}
style={{width:'100%', height: 200, resizeMode : 'stretch'}}
/>
<Text style={styles.info}>{item.jetpack_featured_media_url}</Text>
</View>
)
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
info: {
fontSize: 20,
}
});
Ideally I would like the image to display on the view.
remove single quotes from the Image component's source prop like below :
<Image
source={{uri: item.jetpack_featured_media_url}}
style={{width:'100%', height: 200, resizeMode : 'stretch'}}
/>
If you set 'item.jetpack_featured_media_url' (with a single quote) to the URI it will consider it as a path itself
I'm using DrawerNavigator and, as long as I want to navigate to a new screen, all's fine.
Now I want to add a drawer item which does not navigate to a new screen but simply triggers an action (in general). Specifically, I want to use 'react-native' Share functionality.
I got this to work but I think the solution is not a very good one. Here's what I got so far:
const myContentComponent = props => (
<ScrollView alwaysBounceVertical={false}>
<SafeAreaView style={{ flex: 1 }} forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
<TouchableItem
key="share"
onPress={() => {
Share.share(
{
message: 'YO: this will be the text message',
url: 'http://tmp.com',
title: 'This will be the email title/subject',
},
{
// Android only:
dialogTitle: 'This will be the title in the dialog to choose means of sharing',
},
);
props.navigation.navigate('DrawerClose');
}}
delayPressIn={0}
>
<SafeAreaView forceInset={{ left: 'always', right: 'never', vertical: 'never' }}>
<View style={[{ flexDirection: 'row', alignItems: 'center' }, {}]}>
<View style={[{ marginHorizontal: 16, width: 24, alignItems: 'center' }, { opacity: 0.62 }, {}]}>
<Icon name="share" />
</View>
<Text style={[{ margin: 16, fontWeight: 'bold' }, { color: DrawerItems.defaultProps.inactiveTintColor }]}>Share</Text>
</View>
</SafeAreaView>
</TouchableItem>
</SafeAreaView>
</ScrollView>
);
const RootStack = DrawerNavigator(
{
Settings: {
screen: SettingsScreen,
},
},
{
contentComponent: myContentComponent,
},
);
Basically, I am creating a new contentComponent based off of the default:
https://github.com/react-navigation/react-navigation/blob/v1.5.8/src/navigators/DrawerNavigator.js#L16-L22
I am copying the styles and element structure of a normal drawer item (everything under TouchableItem) - all this so I can define my own onPress which does the share logic and closes the drawer.
There has to be a better way right? What happens if I want the "Share" drawer item somewhere amongst the drawer items rendered by DrawerItems (the ones that support navigation)? Right now I can only work around the items rendered by DrawerItems. Besides, copying so much code from react-navigation seems like really bad form.
I just want an item which does some custom logic instead of rendering a screen.
I am not sure will it be helpful?
I used onPress event for logout like this way. It's no need to render a new DrawerItems
const DrawerNavigator = createAppContainer(createDrawerNavigator({
Logout: {
screen: EmptyScreenForLogoutConfirmation,
navigationOptions: ({ navigation }) => ({
tabBarLabel: 'Logout',
drawerIcon: ({ tintColor }) => <Icon name={"ios-cog"} size={26} />,
}),
},
},
{
contentComponent:(props ) => (
<DrawerItems {...props}
onItemPress = {
( route ) =>
{
if (route.route.routeName !== "Logout") {
props.onItemPress(route);
return;
}
return Alert.alert( // Shows up the alert without redirecting anywhere
'Confirmation required'
,'Do you really want to logout?'
,[
{text: 'No'},
{text: 'Yes', onPress: () => { props.navigation.navigate('Logedout'); }},
]
)
}
}
/>
),
contentOptions: {
activeTintColor: '#e91e63',
}
},))
The value set in asyncstorage is null and also the log for usertext is { [TypeError: undefined is not an object (evaluating 'this.state.usertext')] line: 30, column: 34 }
what might be the problem here?
'use strict'
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Navigator,
Text,
TextInput,
Button,
AsyncStorage,
View
} from 'react-native';
import { Actions } from 'react-native-router-flux';
import api from './utilities/api';
export default class LoginScreen extends Component{
constructor(props) {
super(props);
this.state = { usertext: 'placeholder' , passwordtext: 'placeholder' ,buttonvalue: '
Login'};
}
async login() {
const res = await api.getLoginResult();
const status= res.status;
console.log('log'+status);
if(status==='success'){
try {
console.log('usertext'+this.state.usertext);
await AsyncStorage.setItem('username', this.state.usertext);
var username = await AsyncStorage.getItem('username');
console.log('username'+username);
await AsyncStorage.setItem('password', this.state.passwordtext);
} catch (error) {
// Error saving data
console.log(error);
}
Actions.HomeScreen();
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.containerLogin}>
<View style={styles.headerContainer}>
<Text style={styles.welcome}>PLEASE LOGIN</Text>
</View>
<View style={styles.inputlayouts}>
<TextInput
onChangeText={(usertext) => this.setState({usertext})}
value={this.state.usertext}
/>
<TextInput
onChangeText={(passwordtext) => this.setState({passwordtext})}
value={this.state.passwordtext}
/>
</View>
<View style={styles.buttonView}>
<Button
style={styles.buttonstyle}
onPress={this.login}
title={this.state.buttonvalue}
color="#FF7323">
</Button>
</View>
</View>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'stretch',
backgroundColor: '#1AB591',
},
headerContainer:{
backgroundColor:'#FF7323'
},
buttonView:{
marginLeft:15,
marginRight:15,
marginBottom:15
},
inputlayouts:{
marginLeft:6,
marginRight:6,
marginBottom:10,
},
containerLogin: {
justifyContent: 'center',
alignItems: 'stretch',
backgroundColor: '#FFF',
marginLeft:20,
marginRight:20
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color:'#FFF'
},
buttonstyle: {
width:20,
}
});
Change
<Button
style={styles.buttonstyle}
onPress={this.login}
title={this.state.buttonvalue}
color="#FF7323">
</Button>
to
<Button
style={styles.buttonstyle}
onPress={this.login.bind(this)}
title={this.state.buttonvalue}
color="#FF7323">
</Button>
When you do onPress={this.login}, the method reference to this will be different than your component. Binding this to method, allows you to reference your this in the function.
I have a list that will display a certain number of images in a row. What I want is for there to be space between each image. I was trying to use padding but it has not worked.
Here is what I have:
class ImageList extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
require('./images/ex1.jpg'), require('./images/ex2.jpg'), require('./images/ex3.jpg')
])
};
}
render() {
return (
<View>
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={(rowData) => <Image source={rowData} style={styles.imageSize}/>}
/>
</View>
);
}
}
var styles = StyleSheet.create({
imageSize: {
//newWidth is the width of the device divided by 4.
//This is so that four images will display in each row.
width: newWidth,
height: newWidth,
padding: 2
},
list: {
flexDirection: 'row',
flexWrap: 'wrap'
}
});
In the main file the code is called:
class Main extends Component {
render(){
return(
<ScrollView>
<View style={{flex: 1}}>
<View>
<ImageList/>
</View>
</View>
</ScrollView>
);
}
};
I have tried putting padding in each View that I have to see if any it works. But so far nothing is working.
I have wrapped the rawData with a view like this:
render() {
return (
<View>
<ListView automaticallyAdjustContentInsets={false}
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={(rowData) => <View style={{padding: 1}}><Image source={rowData} style={styles.imageSize}/></View>}
/>
</View>
But this has caused a problem. When I try to have more than 3 images, only the first 3 appear. The rest are nowhere to be found.