Implementation of Flatlist/SectionList of Custom Card view's - ios

I am trying to implement a list of custom card view's(Like the CollectionView in ios with custom collectionview cell). How can I achieve this any idea's or tutorials plz. I do not want to use third party. Could someone help me out.

You can have a Collection View like a solution with React Native FlatList. To make a grid view you have to use numColumns prop. The following code segment will support you.
class MyListItem extends React.PureComponent {
render() {
return (
<View style={styles.item}>
<Text>
{this.props.title}
</Text>
</View>
);
}
}
export default class App extends React.Component {
data = [
{
"id":1,
"label":"Label 01"
},
{
"id":2,
"label":"Label 02"
},
{
"id":3,
"label":"Label 03"
},
{
"id":4,
"label":"Label 04"
},
{
"id":5,
"label":"Label 05"
},
{
"id":6,
"label":"Label 06"
},
{
"id":7,
"label":"Label 07"
},
{
"id":8,
"label":"Label 08"
},
{
"id":9,
"label":"Label 09"
}
]
_keyExtractor = (item, index) => item.id;
renderItem = ({item}) => (
<MyListItem
id={item.id}
title={item.label}
/>
);
render() {
return (
<View style={styles.container}>
<FlatList
data={this.data}
renderItem={this.renderItem}
keyExtractor={this._keyExtractor}
numColumns={3}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
item: {
backgroundColor: '#add8e6',
alignItems: 'center',
justifyContent: 'center',
flex:1,
margin: 5,
height: 100
}
});

Depends on what you are trying to do. In minimum, a FlatList expects the following props;
data: An array of data. (Each data point is one row in your list)
renderItem: How to transform a data point into a row. (Think of your render function, but for just a row.)
keyExtractor: How to create keys(think of them as ids) for these rows to keep track of them.
In the end you would need to do the following (keep in mind data structure, keyExtractor and what is being rendered is completely arbitrary);
<FlatList
data={[{ id: 1, text: 'Hello' }, { id: 2, text: 'StackOverflow!' }]}
keyExtractor={item => `texts#${item.id}`}
renderItem={({ item }) => (<Text>{item.text}</Text>)}
/>
More extensive data about this can be found at official React Native documentation.

Related

React-Native/Expo Receiving ERROR 'null is not an object (evaluating 'match.localteam_name')

Here I am only running two API requests. The first one in the componentDidMount function works fine, but the second one labeled handleMatchFacts does not work. In short, Using React-Native I'm retrieving information from the API, mounting it to the page and then once the Touchablehighlight is clicked it is suppose to retrieve additional information from the API according to the 'id' that is passed in 'onPress'. I am able to console.log the json of the data in the second request, but for some reason when I setState with the new data and render it to the page in ListView, I get an error.
import React from 'react'
import { View, Text, StyleSheet, ListView, TouchableHighlight } from 'react-native'
export default class Main extends React.Component {
constructor() {
super();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
matches: ds.cloneWithRows([]),
matchFacts: ds.cloneWithRows([])
};
this.handleShowMatchFacts.bind(this)
}
componentDidMount(){
fetch("http://api.football-api.com/2.0/matches?match_date=27.04.2017&to_date=27.04.2017&Authorization=565ec012251f932ea4000001fa542ae9d994470e73fdb314a8a56d76")
.then(res => res.json())
.then(matches => {
this.setState({
matches : this.state.matches.cloneWithRows(matches)
})
})
}
handleShowMatchFacts = id => {
console.log('match', id)
return fetch(`http://api.football-api.com/2.0/matches/${id}?Authorization=565ec012251f932ea4000001fa542ae9d994470e73fdb314a8a56d76`)
.then(res => res.json())
.then(matchFacts => {
console.log('match facts', matchFacts)
let selectedMatch = matchFacts;
this.setState({
matches : this.state.matches.cloneWithRows([]),
matchFacts : this.state.matchFacts.cloneWithRows(selectedMatch)
})
})
}
render() {
return (
<View style={styles.mainContainer}>
<Text
style={styles.header}>
Todays Matches</Text>
<ListView
style={styles.matches}
dataSource={this.state.matches}
renderRow={(matches) =>
<TouchableHighlight
onPress={() => this.handleShowMatchFacts(matches.id)}
underlayColor="green"
><Text style={styles.item}> {matches.localteam_name} {matches.localteam_score} - {matches.visitorteam_score} {matches.visitorteam_name} </Text>
</TouchableHighlight>
}
/>
<ListView
style={styles.matches}
dataSource={this.state.matchFacts}
renderRow={(match) =>
<Text style={styles.item}> {match.localteam_name} {match.localteam_score} - {match.visitorteam_score} {match.visitorteam_name} </Text>
}
/>
</View>
);
}
}
const styles = StyleSheet.create({
mainContainer : {
flex: 1,
padding: 20
},
header : {
textAlign: 'center'
},
matches : {
marginTop: 20
},
item : {
borderRadius: 4,
borderWidth: 0.5,
borderColor: 'green',
marginBottom: 5,
padding: 20,
textAlign: 'center',
},
});
You're probably seeing an issue because the second API request doesn't return an array, it returns an object. The cloneWithRows expects an array. Replacing this line
matchFacts : this.state.matchFacts.cloneWithRows(selectedMatch)
with
matchFacts : this.state.matchFacts.cloneWithRows([selectedMatch])
may help, depending on how you render this new data.
That's just a guess since I don't know what error you're receiving.

Error: "Invalid data message - all must be length: 8" - PickerIOS

Edit: it seems that if I comment out the line "this.setState({logged_in: true});", line 63, that I don't get the error. My guess is that something about the way I'm trying to change the content being displayed in the render function based on if the user is logged in or not is what is the cause of this error. Any ideas?
I've been making some slight progress in understanding some very basics of React Native, I feel like. Although my code might not be pretty, up until some recent additions it worked. I'm recieving an error message in the IOS Simulator that reads "Invalid data message - all must be length: 8". It unfortunately isn't giving me any specifics that I understand, such as linenumbers.
I sincerely apologise if this is a repost, I've been looking like crazy on google and stackoverflow to find a solution to this error but have been unsuccessful in my searches.
I've censored the url I've using in fetch since it is an adress for testing within the company in very early stages, but I am 99,99% sure that's not where the problem lies.
My index.ios.js:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
Alert,
TextInput,
TouchableHighlight,
Image,
AlertIOS,
PickerIOS,
} from 'react-native';
var REASONS = {
sick: {
name: "sjuk"
},
vacation: {
name: "semester"
},
child_care: {
name: "vård av barn"
},
parenting: {
name: "föräldraledig"
},
general: {
name: "övrigt"
},
};
export default class mbab_franvaro extends Component {
constructor(props) {
super(props);
this.state = {username: '', password: '', logged_in: false, reason: 'sjuk'};
}
logout(){
this.setState({logged_in: false});
this.username = ""; this.password = "";
}
login(){
if(this.state.username == "" || this.state.password == ""){
AlertIOS.alert("Fel", "Vänligen fyll i alla fält.");
}
else{
fetch("MY_PRIVATAE_COMPANY_URL", {
method: "POST",
headers: {
'Accept': 'application/x-www-form-urlencoded',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: "username=" + this.state.username + "&password=" + this.state.password,
})
.then((response) => response.json())
.then((response) => {
if(JSON.stringify(response.body).replace(new RegExp('"', 'g'), '').match("Inloggad")){
this.username = this.state.username; this.password = this.state.password;
this.setState({logged_in: true});
//AlertIOS.alert("Hej!", "Välkommen " + this.username + "!");
}
else{
AlertIOS.alert(
"Fel",
JSON.stringify(response.body).replace(new RegExp('"', 'g'), '')
);
}
})
.catch((error) => {
AlertIOS.alert("error", error);
})
.done();
}
}
render(){
if(this.state.logged_in){
//sidan för frånvarorapportering
return (
<View style={styles.container}>
/*<TouchableHighlight style={styles.logout_button} onPress={() => this.logout()}>
<Text style={styles.login_button_text}>Logga ut</Text>
</TouchableHighlight>*/
<View style={styles.report_wrapper}>
<Text style={styles.header}>Frånvarorapportering</Text>
<Text>Ange anledning och hur stor del av dagen du blir frånvarande.</Text>
<PickerIOS
selectedValue={this.state.reason}
onValueChange={(reason) => this.setState({reason})}>
{Object.keys(REASONS).map((reason) => (
<PickerItemIOS
key={reason}
value={reason}
label={REASONS[reason].name}
/>
))}
</PickerIOS>
</View>
</View>
);
}
else{
//inloggningssidan
return (
<View style={styles.container}>
<Image resizeMode="center" style={styles.logo} source={require('./app/res/logo_cmyk.png')} />
<TextInput
placeholder="Namn"
autocorrect={false}
style={styles.text_box}
onChangeText={(username) => this.setState({username})}
/>
<TextInput
placeholder="Lösenord"
autocorrect={false}
secureTextEntry={true}
style={styles.text_box}
onChangeText={(password) => {this.setState({password})}}
/>
<TouchableHighlight style={styles.login_button} onPress={() => this.login()}>
<Text style={styles.login_button_text}>Logga in</Text>
</TouchableHighlight>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F4F4F4',
},
report_wrapper: {
flex: 1,
},
logout_button: {
flex: 0,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginLeft: 10,
marginRight: 10,
marginTop: 30,
marginBottom: 2,
padding: 10,
backgroundColor: "#003878"
},
login_button: {
flex: 0,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginLeft: 10,
marginRight: 10,
marginTop: 2,
marginBottom: 2,
padding: 10,
backgroundColor: "#003878"
},
login_button_text: {
color: "white",
fontSize: 20,
flex: 1,
textAlign: "center",
},
logo: {
//flex: 1,
},
text_box: {
height: 40,
flex: 0,
backgroundColor: "white",
marginLeft: 10,
marginRight: 10,
marginTop: 2,
marginBottom: 2,
padding: 10
},
header: {
color: "#84754E",
fontSize: 25,
marginTop: 30,
},
});
AppRegistry.registerComponent('mbab_franvaro', () => mbab_franvaro);
Edit:
Add toString() to error did the trick for me:
.catch((error) => {
AlertIOS.alert("error", error.toString());
})
I was also getting the same error while using ScrollView.
Took me 2 days to figure out the solution.
I wasn't importing ScrollView from react-native.
Check if you've imported the Picker or not. :)

left side of screen untouchable when using NavigatorIOS react-native

I have this very strange bug on my react-native iOS app, when I'm using NavigatorIOS (need it) : there is a 30 points zone on the left of the screen that is not touchable on the first touch (nothing happens on touchableHighlight or TouchableWithoutFeedback) in Listview elements for example...
When I use Navigator, no problem at all, it's specific to NavigatorIOS (and I need it in this part of the app), also tried without any style, same problem.
Haven't seen any github issue or discussion about this bug.
Edit :
Runnable example : https://rnplay.org/apps/E0R2vg
Component code sample:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
NavigatorIOS,
ListView,
TouchableWithoutFeedback,
Dimensions,
} = React;
var myListView = React.createClass({
getInitialState: function() {
return {
ds: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
};
},
componentDidMount: function() {
console.log('feed datasource');
this.setState({
ds: this.state.ds.cloneWithRows([{name: 'one'}, {name:'two'}, {name: 'three'}]),
});
},
render: function() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.ds}
renderRow={this.renderItem}
style={styles.listView}
/>
<TouchableWithoutFeedback onPress={this.nextroute}>
<View style={[styles.pressme, {flex:1}]}>
<Text>Next route to see the issue</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
},
renderItem: function(item) {
return (
<View style={styles.row}>
<TouchableWithoutFeedback onPress={() => alert('pressed')}>
<View style={styles.pressme}>
<Text>x</Text>
</View>
</TouchableWithoutFeedback>
<Text>{item.name} - Item description...</Text>
</View>
);
},
nextroute: function() {
this.props.navigator.push({
title: 'Try press [x] (twice)',
component: myListView,
onLeftButtonPress: () => this.props.navigator.pop(),
});
},
});
var SampleApp = React.createClass({
render: function() {
return (
<NavigatorIOS
style={styles.navcontainer}
initialRoute={{
component: myListView,
title: 'First view is ok',
}}
tintColor="#000000"
barTintColor="#fd7672"
translucent={false}
ref='navios'
/>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 28,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
fontSize: 19,
marginBottom: 5,
},
navcontainer: {
flex: 1,
},
listView: {
flex: 1,
width: Dimensions.get('window').width,
},
row: {
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: '#FFFFFF',
},
pressme: {
margin: 10,
borderWidth: 1,
borderColor: 'red',
padding: 15,
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);

React native listview add item not working

I am new to react native and I was implemented a simple idea in my head. Basically, I am doing a 'todo list' like component which theres a add button below and items can be added. The problem arises after clicking on the add button and the list gets updated and the following xcode warning message appears. And I have realised, after implementing the ListView, that the app in the simulator slows down so much i couldn't even inspect. The alert popup would freeze the UI after some text are entered too, and the entire app needs to be built again since I couldn't do anything. Thanks for all the help!
Main component: SurveyQn
'use strict'
import React, {
Component,
StyleSheet,
Text,
TouchableHighlight,
TextInput,
View,
ListView,
AlertIOS
} from 'react-native';
var LongButton = require('./LongButton.js');
class SurveyQn extends Component {
constructor(props) {
super(props);
this.state = {
options: [{option: 'Pizza'}],
};
}
componentWillMount() {
this.dataSource = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
}
_renderItem(item) {
return (
<LongButton
text={item.option}
onPress={() => {}}
//btnViewStyle={styles.buttonView}
//btnTextStyle={styles.buttonText}
/>
);
}
_addItem() {
AlertIOS.alert(
'Add new option',
null,
[
{
text: 'Add',
onPress: (text) => {
var options = this.state.options;
options.push({option: text})
this.setState({ options: options})
}
},
],
'plain-text'
);
}
render(){
var dataSource = this.dataSource.cloneWithRows(this.state.options);
return (
<View style={styles.container}>
<TextInput
style={styles.question}
placeholder="Question title"
placeholderTextColor="#4B667B"
selectionColor="#4B667B"
onChangeText={(text) => this.setState({text})}/>
<View style={styles.listView}>
<ListView
dataSource={dataSource}
renderRow={this._renderItem.bind(this)}/>
</View>
<TouchableHighlight
onPress={this._addItem.bind(this)}
style={styles.buttonView}
underlayColor='rgba(0,0,0,0)'>
<Text style={styles.buttonText}>
Add option
</Text>
</TouchableHighlight>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
width: 300,
flex :1,
},
listView: {
flex: 1,
},
question: {
height: 30,
fontSize: 20,
fontWeight: "100",
color: '#4B667B',
marginTop: 10,
marginBottom: 10,
},
buttonView: {
width: 300,
paddingVertical: 9,
borderWidth: 1,
borderColor: '#F868AF',
marginBottom: 13,
},
buttonText: {
textAlign: 'center',
fontSize: 25,
color: '#F868AF',
fontWeight: '500'
},
});
ListView item: LongButton
'use strict'
import React, {
Component,
StyleSheet,
Text,
TouchableHighlight,
View,
} from 'react-native';
class LongButton extends Component {
render(){
return (
<TouchableHighlight
onPress={this.props.onPress}
style={this.props.btnViewStyle}
underlayColor='rgba(0,0,0,0)'>
<Text style={this.props.btnTextStyle}>
{this.props.text}
</Text>
</TouchableHighlight>
);
}
}
module.exports = LongButton;
Xcode warning message upon adding item on alert
app[27881:11151280] the behavior of the UICollectionViewFlowLayout is not defined because:
app[27881:11151280] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
app[27881:11151280] The relevant UICollectionViewFlowLayout instance is <_UIAlertControllerCollectionViewFlowLayout: 0x7ff0685b1770>, and it is attached to ; layer = ; contentOffset: {0, 0}; contentSize: {0, 0}> collection view layout: <_UIAlertControllerCollectionViewFlowLayout: 0x7ff0685b1770>.
2016-04-06 07:50:01.545 decisionapp[27881:11151280] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
Updates:
I tried this but its not working either. Could it be the alert causing these problems? Its just taking forever to render the alert after clicking on the btn.
class SurveyQn extends Component {
constructor(props) {
super(props);
this.state = {
options: [{option: 'Pizza'}],
dataSource : new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
componentWillMount() {
var data = this.state.options;
this.state.dataSource.cloneWithRows(data);
}
_renderItem(item) {
return (
{item.option}
);
}
_addItem() {
AlertIOS.alert(
'Add new option',
null,
[
{
text: 'Add',
onPress: (text) => {
var options = this.state.options;
options.push({option: text})
this.setState({ options: options})
}
},
],
'plain-text'
);
}
render(){
return (
<View style={styles.listView}>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderItem.bind(this)}/>
</View>
<TouchableHighlight
onPress={this._addItem.bind(this)}
style={styles.buttonView}
underlayColor='rgba(0,0,0,0)'>
<Text style={styles.buttonText}>
Add option
</Text>
</TouchableHighlight>
</View>
);
}
}
Before diving into complex EcmaScript notation, you can use simple notation. Here is a simple example of ListView. Please go through it and understand how it works.
var API = require('./API');
module.exports = React.createClass({
getInitialState: function(){
return {
rawData: [],
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
loaded: false,
}
},
componentWillMount: function(){
this.loadData();
},
loadData: function(){
API.getItems()
.then((data) => {
this.setState({
rawData: this.state.rawData.concat(data),
dataSource: this.state.dataSource.cloneWithRows(data),
loaded: true,
});
});
},
render: function(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderItem}
style={styles.listView}>
</ListView>
);
},
renderItem: function(item){
return (
<View>
<Text>Custom Item</Text>
</View>
);
},
}
In API.js, I am fetching data from an API.
getItems: function(){
var REQUEST_URL = 'http://api.example.org/item/get?api_key=xxxx;
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
return responseData.results.sort(sortByDate);
});
}
The code may not work, since I have not tested. But you can refer my sample project on github. Repo

How to styles view to a ratio like 16:9 in React Native?

I want to let the red view keep ratio 16:9. I try but failed. I know React Native use Flexbox (Reimplement in Javascript), but I don't know how to do this. Thanks.
Here is my Javascript:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
View,
} = React;
var AwesomeProject = React.createClass({
render: function() {
return (
<View style={styles.container}>
<View style={styles.banner}>
</View>
<View style={styles.items}>
</View>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
},
banner: {
backgroundColor: 'red',
flex: 1,
},
items: {
backgroundColor: 'blue',
flex: 3,
},
});
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
Here is document about Flexbox in React Native:
http://facebook.github.io/react-native/docs/flexbox.html#content
Here is valid style props:
Valid style props: [
"width",
"height",
"top",
"left",
"right",
"bottom",
"margin",
"marginVertical",
"marginHorizontal",
"marginTop",
"marginBottom",
"marginLeft",
"marginRight",
"borderWidth",
"borderTopWidth",
"borderRightWidth",
"borderBottomWidth",
"borderLeftWidth",
"position",
"flexDirection",
"flexWrap",
"justifyContent",
"alignItems",
"alignSelf",
"flex",
"resizeMode",
"backgroundColor",
"borderColor",
"borderRadius",
"tintColor",
"opacity",
"fontFamily",
"fontSize",
"fontWeight",
"fontStyle",
"lineHeight",
"color",
"containerBackgroundColor",
"textAlign",
"writingDirection",
"padding",
"paddingVertical",
"paddingHorizontal",
"paddingTop",
"paddingBottom",
"paddingLeft",
"paddingRight",
"borderTopColor",
"borderRightColor",
"borderBottomColor",
"borderLeftColor",
"overflow",
"shadowColor",
"shadowOffset",
"shadowOpacity",
"shadowRadius",
"transformMatrix",
"rotation",
"scaleX",
"scaleY",
"translateX",
"translateY"
]"
React Native (since 0.40) supports the aspectRatio prop.
You can do:
style={{ aspectRatio: 16/9 }}
See Maintain aspect ratio of image with full width in React Native
You can use on layout function.
class AwesomeProject = extends React.Component<{}> {
constructor(props) {
super(props)
this.state = {width: 0,height:0}
}
onPageLayout = (event) => {
const {width, height} = event.nativeEvent.layout;
this.setState({
width,
height
})
};
render(){
return (
<View style={styles.container}>
<View style={[
styles.banner,
{
height:this.state.width*9/16
}
]}>
</View>
<View style={styles.items}>
</View>
</View>
);
}
});

Resources