FlatList React Native not displaying image - react-native

I am trying to utilise a FlatList for my expo React Native application. I have decided to save my images in a local folder to display on the home page. However, I am getting the error message below and I really do not understand where I am going wrong.
The error message is: "invalid call at line { see *** below for the line concerned }"
const Inspiration = () => {
const handleSelectedImage = (e) => {
console.log('image selected', e);
};
return (
<>
<CreatedImageModal />
<ModalGeneratingImage />
<ExamplePrompt />
<FlatList
contentContainerStyle={{ flexGrow: 1 }}
data={EXAMPLES}
style={styles.list}
numColumns={2}
keyExtractor={(item) => item.id}
ListHeaderComponent={<Prompt />}
renderItem={({ item }) => (
<Image
source={require(item.id)} // ***
onPress={() => handleSelectedImage(item)}
transition={true}
containerStyle={styles.item}
PlaceholderContent={<ActivityIndicator />}
/>
)}
/>
</>
);
};
const styles = StyleSheet.create({
list: {
width: '100%',
backgroundColor: '#000',
},
item: {
aspectRatio: 1,
width: '100%',
flex: 1,
},
});
const EXAMPLES = [
{
id: '../assets/img-1.png',
prompt: 'A synthwave style sunset above the reflecting water of the sea, digital art'
},
{
id: '../assets/img-2.png',
prompt: 'A van Gogh style painting of an American football player'
},
{
id: '../assets/img-3.png',
prompt: '3D render of a cute tropical fish in an aquarium on a dark blue background, digital art'
},
{
id: '../assets/img-4.png',
prompt: 'A cartoon of a cat catching a mouse'
},
{
id: '../assets/img-5.png',
prompt: 'A comic book cover of a superhero wearing headphones'
},
{
id: '../assets/img-6.png',
prompt: 'A hand-drawn sailboat circled by birds on the sea at sunrise'
},
{
id: '../assets/img-7.png',
prompt: 'An expressive oil painting of a basketball player dunking, depicted as an explosion of a nebula'
},
{
id: '../assets/img-8.png',
prompt: 'A hand drawn sketch of a Porsche 911'
},
{
id: '../assets/img-9.png',
prompt: 'A sea otter with a pearl earring" by Johannes Vermeer'
},
];
export default Inspiration;

Try to change your EXAMPLES and Image component as below:
<Image
source={item.id}
onPress={() => handleSelectedImage(item)}
transition={true}
containerStyle={styles.item}
PlaceholderContent={<ActivityIndicator />}
/>
const EXAMPLES = [
{
id: require('../assets/img-1.png'),
prompt: 'A synthwave style sunset above the reflecting water of the sea, digital art'
},
...
]
You need to add require to your data instead of Image component.

Related

Flatlist: A VirtualizedList contains a cell which itself contains more than one VirtualizedList of the same orientation as the parent list

I am getting the following error in my implementation of FlatList
A VirtualizedList contains a cell with more than one VirtualizedList of the same orientation as the parent list. You must pass a unique listKey prop to each sibling list.
VirtualizedList trace:
Child (vertical):
listKey: rootList-header
cellKey: rootList-header
Parent (vertical):
listKey: function listKey(item, index) {
return 'createEX' + index.toString();
}
cellKey: rootList
I am also adding relevant code. The code works when I test using expo, although the error comes up. The app crashes in TestFlight.
This is my FlatList Implementation. If I remove the MetNeedsMenu part, the code works.
<FlatList style={stylesCreate.scrollStyle}
listKey={(item, index) => {return 'createEX' + index.toString()}}
ListHeaderComponent={
<View style={stylesCreate.createForm}>
<Text style={styles.screenTitle}>CREATE NEW{"\n"}ENTRY</Text>
<Text style={stylesCreate.label}>How are you feeling now?</Text>
<Dropdown mood={newMoodBefore} setMood={setMoodBefore}/>
<Text style={stylesCreate.label}>Select the areas of life that made you feel grateful.</Text>
<MetNeedsMenu selectedNeeds={needs} setSelectedNeeds={setNeeds}/>
<InfoButton/>
<Text style={stylesCreate.label}>What are you grateful for?</Text>
<GratitudeInput value={newEntry} placeholder="Write Here..." setValue={setEntry}/>
<Text style={stylesCreate.label}>How are you feeling after doing the activity?</Text>
<Dropdown mood={newMoodAfter} setMood={setMoodAfter}/>
<CustomButton text='CREATE' onPress={addEntry}></CustomButton>
</View>
}
>
</FlatList>
This is my MetNeedsMenu implementation.
const K_OPTIONS = [
{
item: 'Physical Wellbeing:',
id: '1',
},
{
item: 'Peace & Calm',
id: '2',
},
{
item: 'Energizing Moments',
id: '3',
},
{
item: 'Engagement / Flow',
id: '4',
},
{
item: 'Connection',
id: '5',
},
{
item: 'Accomplishment',
id: '6',
},
{
item: 'Meaning / Fulfillment',
id: '7',
},
{
item: 'Others',
id: '8',
}
]
const MetNeedsMenu = ({ selectedNeeds, setSelectedNeeds }) => {
return (
<View style={{ marginBottom: 20}}>
<SelectBox
width={300}
label="Select multiple"
options={K_OPTIONS}
selectedValues={selectedNeeds}
onMultiSelect={onMultiChange()}
onTapClose={onMultiChange()}
hideInputFilter={true}
isMulti
listKey={(item, index) => {return index.toString()}}
labelStyle={{
fontSize: 15,
color: '#0060ff',
}}
containerStyle={{
backgroundColor: '#ffffff',
borderRadius: 5,
}}
arrowIconColor={'blue'} //style for dropdown arrow
optionsLabelStyle={{ //style for labels of options
color: 'black',
paddingLeft: 10,
fontSize: 15,
}}
multiOptionContainerStyle={{ //style multiple selections
backgroundColor: '#0060ff',
}}
multiListEmptyLabelStyle={{ //style for SELECT text
paddingLeft: 10,
}}
toggleIconColor={'#0060ff'} //style color of select icon
selectedItemStyle={{
paddingLeft: 10,
}}
/>
</View>
)
function onMultiChange() {
return (item) => setSelectedNeeds(xorBy(selectedNeeds, [item], 'id'))
}
}
I am guessing the problem lies with MultiNeedsMenu, but I have put listKey there with no success
The error tells you that you have a FlatList or Scrollview inside another FlatList or Scrollview with the same orientation. What is possible to do is to have nested FlatList or ScrollView of different orientation. Here is an example:
<FlatList
data={...}
keyExtracto={...}>
<ScrollView horizontal>
<Text>One</Text>
<Text>Two</Text>
</ScrollView>
</FlatList>

SectionList: props.sections.reduce is not a function

I've been pulling data from my rails API to render in a list of already made contacts. I've been able to render the contact data easily enough on a FlatList, but now I want to order and display the data in an alphabetical SectionList depending on the first letter of the contact name.
I have managed to get rails to give me the JSON correctly (?). But getting stuck rendering the section list. I'm not sure if I need to restructure the JSON response or if I'm missing something simple in renderSectionHeader
This is the JSON:
contacts: {
data: {
J: [
{
id: 1,
email: 'js#me.com',
password_digest: '-',
phone_number: '+1',
name: 'Jay',
circle_id: 'Jay',
bio: null,
created_at: '2020-07-17T11:05:47.479Z',
updated_at: '2020-07-17T20:22:25.821Z'
}
],
K: [
{
id: 1,
email: 'kk#me.com',
password_digest: '-',
phone_number: '+2',
name: 'Kay',
circle_id: 'Kay',
bio: null,
created_at: '2020-07-17T11:05:48.479Z',
updated_at: '2020-07-17T20:22:28.821Z'
}
]
This is the SectionList:
<SectionList style={{width: '100%', marginTop: 10 }}
sections={this.props.contacts.data}
renderItem={({item}) =>
<TouchableOpacity>
<ContactBasic item={item}/>
</TouchableOpacity>}
renderSectionHeader={({section}) => (
<Text style={styles.sectionHeader}>{section.data}</Text>)}
keyExtractor={(item, index) => index}
ListEmptyComponent={() => (
<View>
<Button title="No Contacts Found" type="clear"/>
</View>
)}
/>
This is the ContactBasic Component:
<View style={styles.contactContainer}>
<View style={styles.contact}>
<Image style={styles.image}/>
<View style={styles.contactText}>
<Text style={styles.contactName}>{props.item.name}</Text>
</View>
</View>
</View>
Your json structure is not compatible
you have an object as data not an array
data: {
J: [
{}
}
You will have to convert it to a structure like below
data: [
{title:'J',contacts:[]}
]
When section list calls the reduce function in the array it throws the error.
If you can handle this on the api level it would be easy, otherwise you will have to do this at JS level.

React native Flatlist items with negative margin

I have a simple vertical Flatlist on Android and I would like to render some of its items with a negative margin. The goal is for those items to appear wider than the Flatlist.
Something like this with red being an item of the flatlist:
Unfortunately, the edges of the items are cut by the edge of the Flatlist.
Is there a way to display items that are wider than the Flatlist that renders them?
EDIT:
I know I can achieve the visual on my illustration by adding a margin/padding to every item on the list except the red one. What I would like to know is if it is possible to make a specific item wider than the Flatlist itself (not just wider than the other items)
I would rather stylize the one item that needs to be wider, rather than all the others.
I myself actually never make an item in FlatList with wider scale of the whole flatList cause I know it's a bad idea and it tends to be ugly looking, just imagine an item with an overlapped verticalScrollIndicator. But the better way is that you can add a View like this.
...FlatList tag.....
renderItem={({ item, index }) => {
return (
<View style={{ paddingHorizontal:item.isWider ? 0 : '5%' }}>
....Children
</View>
)
}}
You can also write the statement to work only for some particular item index.
By the way, the scroll indicator will not overlap with any item and it has a controlled and better looking UI.
Whenever you want to render specific items without margin value, the only thing you have to do is override style.
I create a sample application according to your requirements.
import React, { Component } from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text } from 'react-native';
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',
},
];
export default class App extends Component {
renderItems = ({ item }) => (
// Suppose if you want to change margin value & background color of id == 3
<View style={item.id !== 3 ? styles.item : [styles.item, { backgroundColor: 'red', margin: 0 }]}>
<Text>{item.title}</Text>
</View>
)
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={this.renderItems}
keyExtractor={item => item.id}
/>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
item: {
backgroundColor: 'green',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
});
Hope this helps you. Feel free for doubts.
According to the image you provided, and only style the specific list item, I could change align-self, width, and negative margin to achieve what you want.
Apply the specific item
<FlatList
contentContainerStyle={styles.list}
data={data}
renderItem={({ item, index }) => (
<View style={[styles.item, index === 3 && styles.oversize]} />
)}
/>
styles:
oversize: {
backgroundColor: 'orange',
alignSelf: 'stretch',
width: 'auto',
marginHorizontal: -16,
},
Here is working example

How to convert single screen app to tab based app using react-native-navigation?

I am trying my hands on react-native-navigation but I am stuck with a simple problem.
The app has a login page which doesn’t have tabs. (very much similar to facebook login page)(Image ref -
The image is just to give an idea. Image Courtesy -
Google
After the user logs in, I want to convert to a tab based app, and I want different navigation stacks for both tabs (as it happens in apps like Quora)
(Image ref - Again Image is just a reference)
I am using mobx for state management.
I know its a common use case but I am confused between the two options known to me -
provide a reaction to user login variable and change from single screen app to tab based app. (The only concern is the lack of animation when the reaction on isLoggedIn happens)
Eg -
App.js
constructor() {
reaction(() => auth.isLoggedIn, () => app.navigateToHome())
reaction(() => app.root, () => this.startApp(app.root));
app.appInitialized();
}
startApp(root){
switch (root) {
case 'user.login':
Navigation.startTabBasedApp({
tabs: [
{
label: 'One',
screen: 'user.login',
icon: require('./assets/one.png'),
selectedIcon: require('./assets/one_selected.png'),
navigatorStyle: {},
}
]
screen: {
screen: root,
title: 'Login',
navigatorStyle: {
screenBackgroundColor: colors.BACKGROUND,
statusBarColor: colors.BACKGROUND
},
navigatorButtons: {}
}
})
return ;
case 'user.home':
Navigation.startTabBasedApp({
tabs: [
{
label: 'One',
screen: 'user.home',
icon: require('./assets/one.png'),
selectedIcon: require('./assets/one_selected.png'),
navigatorStyle: {},
},
{
label: 'Two',
screen: 'user.home',
icon: require('./assets/two.png'),
selectedIcon: require('./assets/two_selected.png'),
title: 'Screen Two',
navigatorStyle: {},
}
],
animationType: 'slide-down',
});
return;
default:
console.error('Unknown app root');
}
}
Use single screen app but implement tabs in the home screen. With this method, I would have to implement different navigation stack for both tabs by myself. (react-native-navigation already implements this. So, no worries in method 1 regarding navigation stack)
Eg - App.js
Navigation.startSingleScreenApp({
screen: {
screen: root,
title: 'Login',
navigatorStyle: {
screenBackgroundColor: colors.BACKGROUND,
statusBarColor: colors.BACKGROUND
},
navigatorButtons: {}
}
})
Home.js
<Tabs>
<Tab
titleStyle={{fontWeight: 'bold', fontSize: 10}}
selectedTitleStyle={{marginTop: -1, marginBottom: 6}}
selected={true}
renderIcon={() => <Icon containerStyle={{justifyContent: 'center', alignItems: 'center', marginTop: 12}} color={'#5e6977'} name='whatshot' size={33} />}
renderSelectedIcon={() => <Icon color={'#6296f9'} name='whatshot' size={30} />}
>
<Feed />
</Tab>
<Tab
titleStyle={{fontWeight: 'bold', fontSize: 10}}
selectedTitleStyle={{marginTop: -1, marginBottom: 6}}
selected={selectedTab === 'profile'}
title={selectedTab === 'profile' ? 'PROFILE' : null}
renderIcon={() => <Icon containerStyle={{justifyContent: 'center', alignItems: 'center', marginTop: 12}} color={'#5e6977'} name='person' size={33} />}
renderSelectedIcon={() => <Icon color={'#6296f9'} name='person' size={30} />}
onPress={() => this.changeTab('profile')}>
<Profile />
</Tab>
</Tabs>
What are the best practices in managing tabs in react-native?
And in my use case which method I should go with ?
To achieve top tabs, you would need to start a single screen app and then define top tabs. You can do something like this below.
Navigation.startSingleScreenApp({
screen: 'FirstScreen'
title: 'FirstScreenTitle'
topTabs: [
{
screen:'FirstTabScreen'
},
{
screen:'SecondTabScreen'
}
]
});
Register each tabs as individual screens first.
Starting with Navigation.startSingleScreenApp() and then moving to Navigation.startTabBasedApp() (Option 1) is the recommended approach.
The only problem you say you face is that there is no animation in Option 1, but you have that option actually: Add a transition animation to the new navigator object:
Navigation.startTabBasedApp({
// ...
animationType: 'slide' // optional, add transition animation to root change: 'none', 'slide-down', 'fade'
})
Look for the animationType property in the docs, both in startSingleScreenApp and startTabBasedApp.

Is there the way to change white color of a main page mask of "react-native-drawer" component?

As I see in android applications almost all drawers in open state makes a black "mask" above main content.
white mask in "react-native-drawer:
black color example:
It is possible to change the color of this "mask" in "react-native-drawer" component to black?
Was looking for the same thing and found a solution at GitHub.
tweenHandler={ratio => ({
main: {
opacity: 1,
},
mainOverlay: {
opacity: ratio / 2,
backgroundColor: 'black',
},
})}
Tested it out and here's what I got (made it pink for visibility):
<Drawer
tweenHandler={ratio => ({
main: {
opacity: 1,
},
mainOverlay: {
opacity: ratio / 2,
backgroundColor: 'pink',
},
})}
ref={(ref) => { this._drawer = ref; }}
content={ navigationView }
side="right"
panOpenMask={.25}
>
Screenshot: