ReactNative How to keep the scroll view position when the keyboard opened - react-native

I am using React Native's ScrollView and FlatList.
When the keyboard is opened,
I would like to see the same screen as before opening the keyboard.
I thought I could use the scrollTo method depending on the keyboard state
It does not work properly.
Is there a typical implementation of a similar case?
keyboardWillShow(e) {
const { scrollView } = this;
const { scrollPostiion } = this.state;
const { height } = e.endCoordinates;
this.setState({
keyboardHeight: height,
scrollPostiion: scrollPostiion + height,
});
scrollView.scrollTo({ x: 0, y: scrollPostiion + height, animated: false });
}
keyboardWillHide() {
const { scrollView } = this;
const { scrollPostiion, keyboardHeight } = this.state;
this.setState({
keyboardHeight: 0,
scrollPostiion: scrollPostiion - keyboardHeight,
});
scrollView.scrollTo({ x: 0, y: scrollPostiion - keyboardHeight, animated: false });
}
changeNowScrollPosition = (event) => {
this.setState({
scrollPostiion: event.nativeEvent.contentOffset.y,
});
}
<ScrollView
ref={(c) => { this.scrollView = c; }}
keyboardShouldPersistTaps="handled"
pinchGestureEnabled={false}
keyboardDismissMode="interactive"
onScroll={(event) => {
changeNowScrollPosition(event);
}}
onScrollEndDrag={(event) => {
changeNowScrollPosition(event);
}}
scrollEventThrottle={16}
>

use KeyboardAvoidingView :
Following is a simple example:
import React, { Component } from 'react';
import { Text, Button, StatusBar, TextInput, KeyboardAvoidingView, View, StyleSheet } from 'react-native';
import { Constants } from 'expo';
export default class App extends Component {
state = {
email: '',
};
render() {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<KeyboardAvoidingView behavior="padding" style={styles.form}>
<TextInput
style={styles.input}
value={this.state.email}
onChangeText={email => this.setState({email})}
ref={ref => {this._emailInput = ref}}
placeholder="email#example.com"
autoCapitalize="none"
autoCorrect={false}
keyboardType="email-address"
returnKeyType="send"
onSubmitEditing={this._submit}
blurOnSubmit={true}
/>
<View>
<Button title="Sign Up" onPress={this._submit} />
<Text style={styles.legal}>
Some important legal fine print here
</Text>
</View>
</KeyboardAvoidingView>
</View>
);
}
_submit = () => {
alert(`Confirmation email has been sent to ${this.state.email}`);
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: 'white',
},
input: {
margin: 20,
marginBottom: 0,
height: 34,
paddingHorizontal: 10,
borderRadius: 4,
borderColor: '#000000',
borderWidth: 1,
fontSize: 16,
},
legal: {
margin: 10,
color: '#333',
fontSize: 12,
textAlign: 'center',
},
form: {
flex: 1,
justifyContent: 'space-between',
},
});
Please note: styling is important.
You're good to go!

Related

How to Choose File From Button next to input filed in the form in react native. (See image for better understanding)

I am new to react native. and I have created A form. Now in that form I want A choose file button next to the input filed. And when user click on that Choose file button. Either camera will open or gallery will open(as per user choice) and then When user click on choose file button A small image or just image name comes below the choose file button
here is my image for better understanding
here is my code
import React, {useState, Component} from 'react';
import {Picker, Text, StyleSheet, View,
TextInput, Button, KeyboardAvoidingView,
ScrollView, Alert, alert, TouchableOpacity, Dimensions,} from 'react-native';
import { StackNavigator, navigation} from "react-navigation";
import { Card, Badge, Block, } from "../components";
import { theme, mocks } from "../constants";
import DigSign from "./DigSign"
import { Ionicons } from '#expo/vector-icons';
const { height } = Dimensions.get("window");
const { width } = Dimensions.get("window");
class PickerDemo extends Component{
constructor(props) {
super(props);
this.state={
};
}
validateInputs = () => {
// if (!this.state.accountNo.trim())
if (!/[A-Z]{5}[0-9]{4}[A-Z]{1}/.test(this.state.PAN))
{
this.setState({ PANError: 'Please enter valid PAN Number' })
return;
}
if (!/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/.test(this.state.GST))
{
this.setState({ GSTError: 'Please enter valid GST Number' })
return;
}
if (!/^[2-9]{1}[0-9]{3}\\s[0-9]{4}\\s[0-9]{4}$/.test(this.state.Aadhar))
{
this.setState({ AadharError: 'Please enter valid Aadhar Number' })
return;
}
else {
Alert.alert("All fields validated")
return;
}
}
handlePAN = (text) => {
this.setState({ PANError: '' })
this.setState({ PAN: text })
}
handleGST = (text) => {
this.setState({ GSTError: '' })
this.setState({ GST: text })
}
handleAadhar = (text) => {
this.setState({ AadharError: '' })
this.setState({ Aadhar: text })
}
render(){
const offset = (Platform.OS === 'android') ? -200 : 0;
const { navigation } = this.props;
return (
<View style={{flex: 1}}>
<View style={styles.header}>
<Ionicons style={{paddingLeft:20}} name="arrow-back" size={40}
color="black" onPress={() => navigation.navigate("FormItems")} />
<Text style={{fontSize:20, paddingLeft: 70, paddingTop: 10}}>KYC Details</Text>
</View>
<KeyboardAvoidingView keyboardVerticalOffset={offset} style={styles.form} behavior='padding'>
<Text style={styles.formLabel}> OTHER INFORMATION Form </Text>
<ScrollView style={{flex: 1,}} showsVerticalScrollIndicator={false}>
<View style={{flexDirection:'row'}}>
<TextInput maxLength={30} placeholder="PAN Card Number *" style={styles.inputStyle}
onChangeText={this.handlePAN} />
<View style={{justifyContent:"center"}}>
<Button title={'Choose File'}/>
</View>
</View>
<Text>{this.state.PANError}</Text>
<View style={{flexDirection:'row'}}>
<TextInput maxLength={30} placeholder="GSTIN Number*" style={styles.inputStyle}
onChangeText={this.handleGST} />
<View style={{justifyContent:"center"}}>
<Button title={'Choose File'}/>
</View>
</View>
<Text>{this.state.GSTError}</Text>
<View style={{flexDirection:'row'}}>
<TextInput maxLength={30} placeholder="Aadhar Card Number*" style={styles.inputStyle}
onChangeText={this.handleAadhar} />
<View style={{justifyContent:"center"}}>
<Button title={'Choose File'}/>
</View>
</View>
<Text>{this.state.AadharError}</Text>
<TouchableOpacity
onPress={() => navigation.navigate("DigSign")}
>
<Card center middle shadow style={styles.category}>
<Text medium height={1} size={1}>
Digital Signature
</Text>
</Card>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigation.navigate("ImgpickWithCam")}
>
<Card center middle shadow style={styles.category}>
<Text medium height={1} size={1}>
Pick An Image From Camera
</Text>
</Card>
</TouchableOpacity>
</ScrollView>
<View style={{ height: 30 }} />
<Button style={styles.inputStyleB}
title="Submit"
color="#808080"
onPress={() => this.validateInputs()}
/>
</KeyboardAvoidingView>
</View>
);
};
}
const styles = StyleSheet.create({
form: {
flex: 1,
justifyContent: "center",
flex: 1,
backgroundColor: "rgb(247, 146, 57)",
alignItems: 'center',
paddingTop: 50,
},
container: {
flex: 1,
backgroundColor: "rgb(247, 146, 57)",
alignItems: 'center',
// justifyContent: 'center',
paddingTop: 15
},
formLabel: {
fontSize: 20,
color: 'rgb(10, 10, 10)',
},
inputStyle: {
marginTop: 20,
width: 220,
height: 40,
paddingHorizontal: 10,
borderRadius: 50,
backgroundColor: 'rgb(255, 252, 252)',
},
formText: {
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: 20,
},
text: {
color: '#fff',
fontSize: 20,
},
category: {
marginTop: 20,
// this should be dynamic based on screen width
minWidth: (width - theme.sizes.padding * -10 - theme.sizes.base) / 2,
maxWidth: (width - theme.sizes.padding * -10 - theme.sizes.base) / 2,
maxHeight: (height - theme.sizes.padding * -50 - theme.sizes.base) / 2,
},
header:{
flexDirection: 'row'
}
});
export default PickerDemo;
Here is the solution based on the previous examples that I have given you.
You just had to implement the conditional rendering to it.
Just a couple of lines of code what was needed :)
Working Example: Expo Snack
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
View,
Button,
Image,
FlatList,
Text,
TextInput,
} from 'react-native';
import { Camera } from 'expo-camera';
import { Ionicons } from '#expo/vector-icons';
import * as ImagePicker from 'expo-image-picker';
export default function Add() {
const [cameraPermission, setCameraPermission] = useState(null);
const [galleryPermission, setGalleryPermission] = useState(null);
const [showCamera, setShowCamera] = useState(false);
const [camera, setCamera] = useState(null);
const [imageUri, setImageUri] = useState([]);
const [type, setType] = useState(Camera.Constants.Type.back);
const [imageArray, setImageArray] = useState([]);
const permisionFunction = async () => {
// here is how you can get the camera permission
const cameraPermission = await Camera.requestPermissionsAsync();
console.log('camera permission:', cameraPermission.status);
setCameraPermission(cameraPermission.status === 'granted');
const imagePermission = await ImagePicker.getMediaLibraryPermissionsAsync();
console.log('permission:', imagePermission.status);
setGalleryPermission(imagePermission.status === 'granted');
if (
imagePermission.status !== 'granted' &&
cameraPermission.status !== 'granted'
) {
alert('Permission for media access needed.');
}
};
useEffect(() => {
permisionFunction();
}, []);
const takePicture = async () => {
if (camera) {
const data = await camera.takePictureAsync(null);
console.log(data.uri);
setImageUri(data.uri);
setImageArray([...imageArray, data.uri]);
setShowCamera(false);
}
};
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
});
console.log(result.uri);
if (!result.cancelled) {
setImageArray([...imageArray, result.uri]);
}
};
return (
<View style={styles.container}>
{showCamera && (
<Camera ref={(ref) => setCamera(ref)} style={{ flex: 1 }} type={type} />
)}
{showCamera && <Button title={'Click'} onPress={takePicture} />}
{!showCamera && (
<>
<View
style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TextInput placeholder={'Enter PAN Number'} />
<View style={{ flexDirection: 'row' }}>
<Button
title={'Camera'}
onPress={() => {
setShowCamera(true);
}}
/>
<Button title={'Gallery'} onPress={pickImage} />
</View>
</View>
{imageArray.length > 0 && (
<View style={{ height: 110 }}>
<FlatList
horizontal
data={imageArray}
renderItem={({ item }) => (
<Image
source={{ uri: item }}
style={{
width: 100,
height: 100,
borderRadius: 10,
margin: 5,
}}
/>
)}
/>
</View>
)}
</>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
marginTop: 30,
flex: 1,
},
fixedRatio: {
flex: 1,
},
});

Issue with React Native animations

I'm facing a problem with centring the text after the animation finishes as you can see in the video here https://www.youtube.com/watch?v=hhBGUp9_GAY&feature=youtu.be. I want to get both titles perfectly centered horizontally on all devices no matter the screen width. I'm using the Animated API. Any suggestions?
Here is my approach
import React, { useEffect } from "react";
import { View, StyleSheet, Animated, Text, Dimensions, AsyncStorage } from "react-native";
export default function Welcome({ navigation }) {
const width = Dimensions.get('screen').width
let position1 = new Animated.ValueXY(0, 0);
let position2 = new Animated.ValueXY(0, 0);
useEffect(() => {
Animated.timing(position1, {
toValue: { x: width / 4.5, y: 0 },
duration: 900
}).start();
Animated.timing(position2, {
toValue: { x: -width / 3, y: 0 },
duration: 900
}).start();
}, []);
_retrieveData = async () => {
try {
const token = await AsyncStorage.getItem('tokehhn');
if (token !== null) {
// We have data!!
setTimeout(() => navigation.navigate('Home'), 2000)
} else {
setTimeout(() => navigation.navigate('Auth'), 2000)
}
} catch (error) {
// Error retrieving data
}
};
useEffect(() => {
_retrieveData()
}, [])
return (
<View style={styles.container}>
<Animated.View style={position1.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', fontWeight: 'bold', fontSize: 24, color: '#5790f9' }}>Welcome to Glue</Text>
</Animated.View>
<Animated.View style={position2.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', right: -220, fontWeight: 'bold', fontSize: 21, color: '#5790f9' }}>Where everything happens</Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
}
});
Thats how you do it:
let {width} = Dimensions.get('window')
export default function App() {
let animation = new Animated.Value(-width);
let translateX = animation.interpolate({inputRange:[-width,0],outputRange:[2*width,0]});
React.useEffect(()=>{
Animated.timing(animation,{toValue:0}).start();
},[])//eslint-ignore-line
return (
<View style={styles.container}>
<Animated.Text style={[styles.text,{transform:[{translateX:animation}]}]}>LOL</Animated.Text>
<Animated.Text style={[styles.text,{transform:[{translateX}]}]}>Longer LOLLLL</Animated.Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
text:{
textAlign:'center'
}
});
I have created snack as well
Make it a simple and clean interpolation.
The code looks always clean, and readable if we use Animated.Value in range of 0 - 1.
Full code:
import React, {useEffect} from 'react';
import {View, StyleSheet, Animated} from 'react-native';
const App = () => {
const animate = new Animated.Value(0);
const inputRange = [0, 1];
const translate1 = animate.interpolate({inputRange, outputRange: [-100, 0]});
const translate2 = animate.interpolate({inputRange, outputRange: [100, 0]});
useEffect(() => {
Animated.timing(animate, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<View style={styles.container}>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate1}]}]}>
First Text
</Animated.Text>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate2}]}]}>
Second Text
</Animated.Text>
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 25,
},
});
Using that animated value, implement any other animations if needed.
For example, If you need to scale the text while moving:
const scale = animate.interpolate({inputRange, outputRange: [1, 1.5]});

react native bar code reader is not working correctly?

Problem:
I have created a react native application. There I am using expo-barcode-scanner. This is how my code is organized.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
TextInput,
ScrollView,
KeyboardAvoidingView,
} from "react-native";
import Dimensions from "Dimensions";
import * as Permissions from "expo-permissions";
import { BarCodeScanner } from "expo-barcode-scanner";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
QrPress: false,
hasCameraPermission: null,
};
}
componentDidMount() {
this.getPermissionsAsync();
}
getPermissionsAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === "granted" });
};
_onPress_QrScan() {
this.setState({
QrPress: true
});
}
handleBarCodeScanned = ({ type, data }) => {
this.setState({ QrPress: false, scanned: true, lastScannedUrl: data });
};
renderBarcodeReader() {
const { hasCameraPermission, scanned } = this.state;
if (hasCameraPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: "column",
justifyContent: "flex-end"
}}
>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => this.setState({ scanned: false })}
/>
)}
</View>
);
}
render() {
const { hasCameraPermission, scanned, QrPress } = this.state;
let marker = null;
if (this.state.locationChosen) {
marker = <MapView.Marker coordinate={this.state.focusedLocation} />;
}
return (
<View style={{}}>
<KeyboardAvoidingView behavior="padding" enabled>
<ScrollView>
<TouchableOpacity
onPress={() => {
this._onPress_QrScan();
}}
activeOpacity={3}
>
<Text style={styles.viewDetails}>Scan QR</Text>
</TouchableOpacity>
{QrPress ? (
<React.Fragment>{this.renderBarcodeReader()}</React.Fragment>
) : (
null
)}
</ScrollView>
</KeyboardAvoidingView>
</View>
);
}
}
const DEVICE_WIDTH = Dimensions.get("window").width;
const DEVICE_HEIGHT = Dimensions.get("window").height;
const styles = StyleSheet.create({
container: {
top: 0,
flex: 3
},
map: {
flex: 1,
height: 130
},
homeHeader: {
flexDirection: "column",
flex: 1
},
homeHeaderImage: {
flexDirection: "row"
},
homeHederText: {
fontSize: 18,
fontWeight: "bold",
fontStyle: "normal",
fontFamily: "sans-serif",
letterSpacing: 0.81,
color: "#000104",
marginTop: "2%",
marginLeft: "40%",
marginRight: "3%"
},
hederContentContainer: {
flexDirection: "row",
marginTop: "30%",
marginBottom: "10%"
},
qrCodeGeneraterContainer: {
alignItems: "center",
justifyContent: "center"
},
});
export default Home;
But when I open the app with expo client on my android mobile. It is not rendering the barcode reader. It means It is not opening the camera to scan the QR. It just only shows a blank white background. I tried a lot to find out the solution to this problem. Unfortunately, I could not do anything with this issue. Can someone help me with this problem? Thank you.
There are a lot's of issue in your component. Please use the below code and update the style etc depend on your requirements.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
TextInput,
ScrollView,
KeyboardAvoidingView,
Dimensions,
Button,
} from "react-native";
import MapView from 'react-native-maps';
import * as Permissions from "expo-permissions";
import { BarCodeScanner } from "expo-barcode-scanner";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
QrPress: false,
hasCameraPermission: null,
};
}
componentDidMount() {
this.getPermissionsAsync();
}
getPermissionsAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === "granted" });
};
_onPress_QrScan = () => {
this.setState({
QrPress: true
});
}
handleBarCodeScanned = ({ type, data }) => {
this.setState({ QrPress: false, scanned: true, lastScannedUrl: data });
};
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
if (hasCameraPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: "column",
justifyContent: "flex-end",
}}
>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1, ...StyleSheet.absoluteFillObject}}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => this.setState({ scanned: false })}
/>
)}
</View>
);
}
render() {
const { hasCameraPermission, scanned, QrPress } = this.state;
let marker = null;
if (this.state.locationChosen) {
marker = <MapView.Marker coordinate={this.state.focusedLocation} />;
}
return (
<View style={{flex:1}}>
<KeyboardAvoidingView behavior="padding" enabled style={{flex:1}}>
<ScrollView contentContainerStyle={{flexGrow: 1}} >
{QrPress ? (
<View style={{flex:1}}>
{this.renderBarcodeReader()}
</View>
) : (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<TouchableOpacity
onPress={this._onPress_QrScan}
activeOpacity={3}
>
<Text style={styles.viewDetails}>Scan QR</Text>
</TouchableOpacity>
</View>
)}
</ScrollView>
</KeyboardAvoidingView>
</View>
);
}
}
const DEVICE_WIDTH = Dimensions.get("window").width;
const DEVICE_HEIGHT = Dimensions.get("window").height;
const styles = StyleSheet.create({
container: {
top: 0,
flex: 3
},
map: {
flex: 1,
height: 130
},
homeHeader: {
flexDirection: "column",
flex: 1
},
homeHeaderImage: {
flexDirection: "row"
},
homeHederText: {
fontSize: 18,
fontWeight: "bold",
fontStyle: "normal",
fontFamily: "sans-serif",
letterSpacing: 0.81,
color: "#000104",
marginTop: "2%",
marginLeft: "40%",
marginRight: "3%"
},
hederContentContainer: {
flexDirection: "row",
marginTop: "30%",
marginBottom: "10%"
},
qrCodeGeneraterContainer: {
alignItems: "center",
justifyContent: "center"
},
});
export default Home;

React native search onChange input text, code structure

I am building a searchBar, whenever I do search I get undefined error because the value doesn't exist in state till I finish the whole value so I know that I will get error yet I am unable to solve it so I am trying to render cards according to the search input I think I did hard code my homeScreen I am not sure if I am doing it even right and here it comes the question to the three if statements inside render that I have is it good practice ? is it professional ? can i do something else which makes code easier to read and shorter ? I was thinking of eliminating the third if but I wasn't able to change state inside the second if so I had to add the toggle search function to let it work any ideas on how to eliminate the third if would be nice ..! thank you in advance guys
homeScreen.js
import axios from 'axios';
import React from 'react';
import {
ActivityIndicator,
ScrollView,
Text,
View,
TouchableOpacity,
TextInput,
} from 'react-native';
import Card from '../Components/Card/card';
export default class HomeScreen extends React.Component {
state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
componentDidMount() {
this.getData();
}
toggleSearch = () => {
console.log('hlelleloe');
this.setState({
search: true,
});
};
getData = () => {
const requestUrls = Array.from({length: 9}).map(
(_, idx) => `http://api.tvmaze.com/shows/${idx + 1}`,
);
const handleResponse = data => {
this.setState({
isLoading: false,
shows: data,
});
};
const handleError = error => {
console.log(error);
this.setState({
isLoading: false,
});
};
Promise.all(requestUrls.map(url => axios.get(url)))
.then(handleResponse)
.catch(handleError);
};
render() {
const {isLoading, shows, search, title} = this.state;
if (isLoading) {
return <ActivityIndicator size="large" color="#0000ff" />;
} else if (!search) {
return (
<View>
<View>
<TouchableOpacity
onPress={this.toggleSearch}
style={{height: 300, width: 300}}>
<Text style={{textAlign: 'center', fontSize: 40}}>
Press to Search
</Text>
</TouchableOpacity>
</View>
<ScrollView style={{backgroundColor: '#E1E8E7'}}>
{shows.length &&
shows.map((show, index) => {
return (
<Card
key={show.data.id}
title={show.data.name}
rating={show.data.rating.average}
source={show.data.image.medium}
genres={show.data.genres}
language={show.data.language}
network={show.data.network}
schedule={show.data.schedule}
summary={show.data.summary}
navigation={this.props.navigation}
/>
);
})}
</ScrollView>
</View>
);
} else if (search) {
console.log(title);
return (
<View>
<TextInput
style={{
height: 100,
width: 100,
borderColor: 'gray',
borderWidth: 1,
}}
onChangeText={searchedTitle => (
<Card title={shows.data.searchedTitle} />
)}
/>
</View>
);
}
}
}
Card.js
import React from 'react';
import {
Image,
View,
Text,
Button,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from 'react-native-responsive-screen';
import Icon from 'react-native-vector-icons/FontAwesome';
const Card = props => {
return (
<View style={styles.container}>
<Image style={styles.Image} source={{uri: `${props.source}`}} />
<Text style={styles.title}>{props.title}</Text>
<View style={styles.ratingContainer}>
<Text style={styles.rating}>Rating: {props.rating}</Text>
<Icon name="star" size={30} color="grey" />
</View>
<TouchableOpacity
style={styles.button}
onPress={() => {
props.navigation.navigate('Details', {
title: props.title,
rating: props.rating,
source: props.source,
genres: props.genres,
language: props.language,
network: props.network,
schedule: props.schedule,
summary: props.summary,
});
}}>
<Text style={styles.buttonText}>Press for details </Text>
</TouchableOpacity>
</View>
);
};
export default Card;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
Image: {
flex: -1,
width: wp('90%'),
height: hp('65%'),
},
title: {
flex: 1,
fontSize: 40,
borderRadius: 10,
color: '#3C948B',
margin: 15,
justifyContent: 'center',
alignItems: 'center',
},
ratingContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'white',
elevation: 6,
justifyContent: 'space-between',
borderWidth: 1,
width: 300,
},
rating: {
fontSize: 25,
paddingLeft: 15,
},
button: {
flex: 1,
color: '#3C948B',
backgroundColor: '#3C948B',
height: hp('7%'),
width: wp('70%'),
margin: 20,
alignItems: 'center',
borderBottomLeftRadius: 10,
borderTopRightRadius: 10,
},
buttonText: {
flex: 1,
fontSize: 25,
},
});
you Need to implement a constructor for your React component.
Typically, in React constructors are only used for two purposes:
Initializing local state by assigning an object to this.state
Binding event handler methods to an instance
Do
state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
replace this with
constructor(props){
super(props);
this.state = {
shows: [],
isLoading: true,
search: false,
title: '',
};
}

Cannot read property `navigation` of undefined

The following code all works except for when I click on the last SettingsList.Item component inside of the SettingsRoute. The error I get is "Cannot read property navigation of undefined".
import React from "react";
import {
View,
TextInput,
Alert,
KeyboardAvoidingView,
Dimensions,
AsyncStorage,
ActivityIndicator,
BackHandler
} from "react-native";
import { withNavigation } from "react-navigation";
import PTRView from "react-native-pull-to-refresh";
import SettingsList from "react-native-settings-list";
import { Text, Toast, Root } from "native-base";
import {
BottomNavigation,
Surface,
Button,
List,
Headline,
Switch,
Divider,
Paragraph,
Dialog,
Portal
} from "react-native-paper";
import { Home } from "../components/";
let width = Dimensions.get("window").width;
let height = Dimensions.get("window").height;
const HomeRoute = () => (
<PTRView onRefresh={() => this.refresh}>
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Logged in:</Text>
<Card name="Hello World" color="#d50000" letterGrade="ABC" grade="123" />
</View>
</PTRView>
);
const Card = props => (
<Surface
style={{
margin: 4,
padding: 8,
backgroundColor: props.color,
elevation: 3,
justifyContent: "center",
width: width - 21,
height: height / 9
}}
>
<Text
style={{
textAlign: "right",
alignSelf: "stretch",
color: "white",
fontSize: responsiveFontSize(2.2)
}}
>
{props.grade}
</Text>
<Text
style={{
textAlign: "left",
color: "white",
fontSize: responsiveFontSize(2.6)
}}
>
{props.name}
</Text>
<Text
style={{
textAlign: "right",
alignSelf: "stretch",
color: "white",
fontSize: responsiveFontSize(2.3)
}}
>
{props.letterGrade}
</Text>
</Surface>
);
const SettingsRoute = () => (
<View style={{ flex: 1, width: width - 9, justifyContent: "center" }}>
<SettingsList backgroundColor="transparent">
<SettingsList.Header
headerText="Settings"
headerStyle={{ color: "black" }}
/>
<SettingsList.Item
itemWidth={50}
title="About/Credits"
onPress={() => Alert.alert("Test")}
/>
<SettingsList.Item
itemWidth={50}
title="Help"
onPress={() => Alert.alert("TEST")}
/>
<SettingsList.Item
itemWidth={50}
title="Logout"
onPress={() => this.props.navigation.navigate("chooseDistrict")}
/>
</SettingsList>
</View>
);
const responsiveFontSize = f => {
return Math.sqrt(height * height + width * width) * (f / 100);
};
export class Grades extends React.Component {
static navigationOptions = {
title: "Grades",
headerLeft: null,
gesturesEnabled: false
};
constructor() {
super();
this.state = {
index: 0,
isAuthed: false,
routes: [
{ key: "home", title: "Home", icon: "home"},
{
key: "settings",
title: "Settings",
icon: "settings",
}
],
visible: false
};
}
_handleIndexChange = index => this.setState({ index });
_renderScene = BottomNavigation.SceneMap({
home: HomeRoute,
settings: SettingsRoute
});
refresh() {
//get grades and repopulate tabs
//refresh
this.setState({ isAuthed: true });
}
componentWillMount() {
BackHandler.addEventListener("hardwareBackPress", function() {
return true;
});
}
pleaseLogout() {
this.props.navigation.navigate("login");
}
render() {
return (
<Root>
<BottomNavigation
styles={{ backgroundcolor: "#8499B1" }}
navigationState={this.state}
onIndexChange={this._handleIndexChange}
renderScene={this._renderScene}
/>
</Root>
);
}
}
export default withNavigation(Grades);
Do I need to pass props into the function?
Am I even defining my variables in the right place?
Thanks in advance
I made a very silly typo.
onPress={() => this.props.navigation.navigate("login")} <- this does not work (login is not capitalized)
onPress={() => this.props.navigation.navigate("Login")} <- this works!