I'm using react native. The images are in svg, so I'm using "react-native-svg" library. The images are working normally in the web, but when I try to show in mobile appear this crazy error:
Expected closing tag </head> to match opening tag <meta> (6:4). If this is valid SVG, it's probably a bug. Please raise an issue
</head>
^
Here where I'm trying to show the images:
<View style={styles.itemsContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{paddingHorizontal: 20}}
>
{items.map(item => (
<TouchableOpacity
key={String(item.id)}
style={[
styles.item,
selectedItems.includes(item.id) ? styles.selectedItem : {}
]}
onPress={() => handleSelectItem(item.id)}
activeOpacity={0.6}
>
<SvgUri width={42} height={42} uri={item.image_url}/>
<Text style={styles.itemTitle}>{item.name}</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
This is the entire code:
import React, {useState, useEffect} from 'react'
import {View, StyleSheet, TouchableOpacity, Text, ScrollView, Image, Alert} from "react-native";
import {Feather as Icon} from '#expo/vector-icons';
import {useNavigation, useRoute} from '#react-navigation/native';
import Constants from 'expo-constants';
import api from '../../services/api';
import MapView, {Marker} from 'react-native-maps';
import {SvgUri} from 'react-native-svg';
import * as Location from 'expo-location';
interface Item {
id: number;
name: string;
image_url: string
}
interface Point {
id: number;
name: string;
image: string;
latitude: number;
longitude:number;
image_url: string;
}
interface Params {
uf: string,
city: string,
}
const Points = () => {
const navigation = useNavigation();
const [items, setItems] = useState<Item[]>([]);
const [selectedItems, setSelectedItems] = useState<number[]>([]);
const [initialPosition, setInitialPosition] = useState<[number, number]>([0, 0]);
const [points, setPoints] = useState<Point[]>([]);
const route = useRoute();
const routeParams = route.params as Params;
useEffect(() => {
api.get('items').then(response => {
setItems(response.data);
});
}, []);
useEffect(() => {
async function loadPosition() {
const { status } = await Location.requestPermissionsAsync();
if (status !== 'granted') {
Alert.alert('Oooops...', 'precisamos de sua permissão para mostrarmos sua localização');
return ;
}
const location = await Location.getCurrentPositionAsync();
const { latitude, longitude } = location.coords;
setInitialPosition([
latitude,
longitude
]);
}
loadPosition();
}, []);
useEffect(() => {
api.get('points', {
params: {
city: routeParams.city,
uf: routeParams.uf,
items: selectedItems,
},
}).then((response) => {
setPoints(response.data)
})
}, [selectedItems]);
function handleNavigateBack() {
navigation.goBack();
}
function handleNavigateToDetail(id: number) {
navigation.navigate('Detail', { point_id: id})
}
function handleSelectItem(id: number) {
const alreadySelected = selectedItems.findIndex(item => item === id );
if (alreadySelected >= 0) {
const filteredItems = selectedItems.filter(item => item !== id);
setSelectedItems(filteredItems)
} else {
setSelectedItems([...selectedItems, id])
}
}
return (
<>
< View style={styles.container}>
<TouchableOpacity onPress={handleNavigateBack}>
<Icon name="arrow-left" size={20} color="#34cb79"/>
</TouchableOpacity>
<Text style={styles.title}>Bem Vindo.</Text>
<Text style={styles.description}>Encontre no mapa um ponto de coleta</Text>
<View style={styles.mapContainer}>
{ initialPosition[0] !== 0 && (
<MapView
style={styles.map}
loadingEnabled={initialPosition[0] === 0}
initialRegion={{
latitude: initialPosition[0],
longitude: initialPosition[1],
latitudeDelta: 0.014,
longitudeDelta: 0.014 ,
}} >
{points.map(point => (
<Marker
key={String(point.id)}
style={styles.mapMarker}
onPress={() => handleNavigateToDetail(point.id)}
coordinate={{
latitude: point.latitude,
longitude: point.longitude,
}}>
<View style={styles.mapMarkerContainer}>
<Image style={styles.mapMarkerImage} source={{uri: point.image_url}}/>
<Text style={styles.mapMarkerTitle}>{point.name}</Text>
</View>
</Marker>
))}
</MapView>
) }
</View>
</View>
<View style={styles.itemsContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{paddingHorizontal: 20}}
>
{items.map(item => (
<TouchableOpacity
key={String(item.id)}
style={[
styles.item,
selectedItems.includes(item.id) ? styles.selectedItem : {}
]}
onPress={() => handleSelectItem(item.id)}
activeOpacity={0.6}
>
<SvgUri width={42} height={42} uri={item.image_url}/>
<Text style={styles.itemTitle}>{item.name}</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
</>
)
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 32,
paddingTop: 20 + Constants.statusBarHeight,
},
title: {
fontSize: 20,
fontFamily: 'Ubuntu_700Bold',
marginTop: 24,
},
description: {
color: '#6C6C80',
fontSize: 16,
marginTop: 4,
fontFamily: 'Roboto_400Regular',
},
mapContainer: {
flex: 1,
width: '100%',
borderRadius: 10,
overflow: 'hidden',
marginTop: 16,
},
map: {
width: '100%',
height: '100%',
},
mapMarker: {
width: 90,
height: 80,
},
mapMarkerContainer: {
width: 90,
height: 70,
backgroundColor: '#34CB79',
flexDirection: 'column',
borderRadius: 8,
overflow: 'hidden',
alignItems: 'center'
},
mapMarkerImage: {
width: 90,
height: 45,
resizeMode: 'cover',
},
mapMarkerTitle: {
flex: 1,
fontFamily: 'Roboto_400Regular',
color: '#FFF',
fontSize: 13,
lineHeight: 23,
},
itemsContainer: {
flexDirection: 'row',
marginTop: 16,
marginBottom: 32,
},
item: {
backgroundColor: '#fff',
borderWidth: 2,
borderColor: '#eee',
height: 120,
width: 120,
borderRadius: 8,
paddingHorizontal: 16,
paddingTop: 20,
paddingBottom: 16,
marginRight: 8,
alignItems: 'center',
justifyContent: 'space-between',
textAlign: 'center',
},
selectedItem: {
borderColor: '#34CB79',
borderWidth: 2,
},
itemTitle: {
fontFamily: 'Roboto_400Regular',
textAlign: 'center',
fontSize: 13,
},
});
export default Points;
Related
I have implemented tensor-flow camera functionalities for pose detection on a website and then hosted it on netlify. Link: https://uactivsite-mobile.netlify.app/.Also github link of the same: https://github.com/viveksgonal/uactivsite/blob/main/src/App.js
I am using this as webview on react-native app. The first time the app builds perfectly and the camera starts. But whenever I try to reload it or run npx react-native start the second time, the camera never opens.
If anyone knows where I'm going wrong, it would be pleasure if you provide the solution. Thank you.
Code is attached below for the react-native app part:
/* eslint-disable react-native/no-inline-styles */
import React, { useRef, useState } from 'react';
const exercises = [
{
name: 'High Knees',
total: 20,
index: 0
},
{
name: 'Jumping Jacks',
total: 25,
index: 1
},
{
name: 'Squats',
total: 20,
index: 2
},
]
import WebView from 'react-native-webview'
import {
View,
StyleSheet,
Text,
Image,
TouchableWithoutFeedback,
Modal
} from 'react-native';
import { useNavigation } from '#react-navigation/native';
const ExerciseCamera = () => {
const webviewRef = useRef(null)
const navigation = useNavigation();
const [speed, setSpeed] = useState(0)
const [reps, setReps] = useState(0)
const ex = 1
function getInjectableJSMessage(message) {
return `
(function() {
window.dispatchEvent(new MessageEvent('message', {
data: ${JSON.stringify(message)}
}));
})();
`;
}
function onMessage(data) {
let val = JSON.parse(data.nativeEvent.data)
if (val.type === 'reps') {
setReps(val.data.rep)
if (val.data.speed !== 0) {
setSpeed(val.data.speed)
}
}
else if (val.type === 'completed') {
navigation.navigate('dashboard', {
screen: 'completeddailyexercise',
});
}
else {
console.log(val.data.rep)
}
}
function sendDataToWebView(msg) {
webviewRef.current.injectJavaScript(
getInjectableJSMessage(msg)
);
}
return (
<View style={styles.container}>
<Modal
transparent={true}
visible={true}
>
<View style={styles.top_container}>
<TouchableWithoutFeedback
onPress={() => {
navigation.navigate('dashboard', {
screen: 'completeddailyexercise',
});
}}>
<Image
style={styles.icons_container}
source={require('../../Assets/play.png')}
/>
</TouchableWithoutFeedback>
<View style={styles.exercise_name_container}>
<Text style={styles.exercise_name}>Side lunges</Text>
</View>
<TouchableWithoutFeedback
onPress={() => {
navigation.navigate('dashboard', { screen: 'dailychallange' });
}}>
<View style={styles.icons_container}>
<Image
style={styles.icon}
source={require('../../Assets/close.png')}
/>
</View>
</TouchableWithoutFeedback>
</View>
<View style={styles.bottom_container}>
<View style={styles.timer_container}>
<Text style={styles.timer_text}>02:47</Text>
</View>
{reps > 0 ? (
<View
style={[
styles.number_container,
{ justifyContent: speed > 0 ? 'space-between' : 'center' },
]}>
{speed > 0 ? <Text style={styles.number}>{speed} RS</Text> : null}
<Text style={styles.number}>{reps}/{exercises[ex].total}</Text>
</View>
) : null}
</View>
</Modal>
<WebView
ref={webviewRef}
mediaPlaybackRequiresUserAction={false}
source={{ uri: 'https://uactivsite-mobile.netlify.app/' }}
scalesPageToFit={false}
mixedContentMode="compatibility"
onMessage={onMessage}
onLoad={event => {
sendDataToWebView({
data: exercises[ex],
type: 'exercise'
})
}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1 },
preview: {
flex: 1,
},
top_container: {
zIndex: 1,
position: 'absolute',
top: 43,
paddingHorizontal: 20,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
},
bottom_container: {
zIndex: 1,
position: 'absolute',
bottom: 0,
width: '100%',
},
number: { color: 'white', fontSize: 28 },
exercise_name_container: {
height: 40,
width: 155,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 20,
backgroundColor: 'rgba(255,255,255,0.2)',
},
number_container: {
height: 62,
backgroundColor: 'black',
width: '100%',
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 19,
},
timer_container: {
width: '100%',
height: 78,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
backgroundColor: 'rgba(255,255,255,0.45)',
alignItems: 'center',
},
timer_text: { color: 'black', fontSize: 48, fontFamily: 'Poppins-Bold' },
icons_container: {
height: 40,
width: 40,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 50,
backgroundColor: 'rgba(255,255,255,0.8)',
},
icon: { width: 16, height: 16, resizeMode: 'contain' },
exercise_name: { color: 'black', fontFamily: 'Poppins-SemiBold', fontSize: 23 },
});
export default ExerciseCamera;
I am using react-native-bouncy-checkbox and Flatlist.
I have created an array object which has id, name, amount.
So far I have achieved:
User can select individual items from the Flatlist, and it will add the amount and display it
as total amount.
User can also edit the amount they have selected using TextInput.
However, I am trying to create a 'Select All' feature.
So, when user presses 'Select All' or taps on the 'checkbox' it should:
select all the items from the FlatList
add the total amount
allow user to edit all the selected amount individually
update the checkbox to show that it is selected.
So far I have tried getting all the 'checkbox' to show that it is selected when 'Select All' text is pressed or when 'checkbox' is pressed (beside select all text).
I have been trying to get this to work for the last couple of hours but couldn't manage to do it. So any help regarding this issue are most welcome.
CODE SNIPPETS AND APP SCREENSHOTS PROVIDED BELOW:
Code Sample:
import 'react-native-gesture-handler';
import React, { useState, useEffect, Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity, FlatList } from 'react-native';
import { Button, Divider, TextInput } from 'react-native-paper';
import BouncyCheckbox from 'react-native-bouncy-checkbox';
import TextInputMask from 'react-native-text-input-mask';
function AccountMultiplePayment({ navigation }) {
const [apiData, setApiData] = useState([
{
id: 1,
name: 'John',
address: 'address 1',
amount: '79.90',
},
{
id: 2,
name: 'Simon',
address: 'address 2',
amount: '35.50',
},
{
id: 3,
name: 'Tim',
address: 'address 3',
amount: '15.50',
},
{
id: 4,
name: 'Rob',
address: 'address 4',
amount: '33.33',
},
{
id: 5,
name: 'Sarah',
address: 'address 5',
amount: '77.77',
},
])
const [billPaymentAmount, setBillPaymentAmount] = useState({})
const [selectedBill, setSelectedBill] = useState([]);
const [totalPaymentAmount, setTotalPaymentAmount] = useState(0);
const computeBillPaymentAmount = () => {
let newBillPaymentAmount = {}
apiData.forEach(({ id, amount }) => {
newBillPaymentAmount[id] = amount
})
return newBillPaymentAmount
}
const computeTotalPaymentAmount = () => {
let total = 0
selectedBill.forEach(id => {
total += parseFloat(billPaymentAmount[id])
})
// Prevent NaN issue, because once user delete amount will become empty string
return total ? total : 0
}
useEffect(() => {
setBillPaymentAmount(computeBillPaymentAmount())
}, [apiData])
useEffect(() => {
setTotalPaymentAmount(computeTotalPaymentAmount())
}, [selectedBill, billPaymentAmount])
const [checked, setChecked] = useState(false);
return (
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
style={{ alignItems: 'center', paddingVertical: 10 }}
onPress={() => setChecked(!checked)}
>
<Text style={{ color: 'black', fontSize: 25 }}>Select All</Text>
</TouchableOpacity>
<BouncyCheckbox
isChecked={checked}
fillColor={'green'}
unfillColor={'#FFFFFF'}
onPress={() => {
setChecked(!checked)
}}
/>
</View>
<FlatList
data={apiData}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: 'row' }}>
<View style={[styles.subHeaderContainer, { flex: 1 }]}>
<Text
style={[
styles.defaultText,
{ fontWeight: 'bold', fontSize: 16 },
]}>
{item.name}
</Text>
<Divider style={{ marginVertical: 5 }} />
<View style={{ flexDirection: 'row' }}>
<Text
style={[styles.defaultText, { fontWeight: 'bold', flex: 2 }]}>
Total Payable Amount:
</Text>
<View style={{ flex: 1 }}>
<TextInput
value={billPaymentAmount[item.id]}
onChangeText={value => setBillPaymentAmount({ ...billPaymentAmount, [item.id]: value })}
keyboardType={'numeric'}
mode={'outlined'}
label={'RM'}
dense={true}
render={props =>
<TextInputMask
{...props}
mask='[9990].[99]'
/>
}
/>
</View>
</View>
</View>
<BouncyCheckbox
isChecked={checked}
fillColor={'green'}
unfillColor={'#FFFFFF'}
onPress={() => {
if (selectedBill.includes(item.id)) {
setSelectedBill(selectedBill.filter(value => value !== item.id))
} else {
setSelectedBill([...selectedBill, item.id])
}
}}
/>
</View>
);
}}
keyExtractor={item => item.id}
removeClippedSubviews={false}
/>
{
<>
<View
style={{
paddingVertical: 10,
paddingHorizontal: 20,
flexDirection: 'row',
backgroundColor:'blue'
}}>
<Text
style={{ color: 'white', flex: 1, fontWeight: 'bold', fontSize: 18 }}>
Total Amount:{' '}
</Text>
<View>
<Text style={{ color: 'white', fontWeight: 'bold', fontSize: 24 }}>
RM {totalPaymentAmount.toFixed(2)}
</Text>
{totalPaymentAmount <= 0 ? null : (
<TouchableOpacity
onPress={() => {
//navigation.goBack();
navigation.goBack();
navigation.navigate('Account');
}}>
<Text>Reset</Text>
</TouchableOpacity>
)}
</View>
</View>
<Button
mode={'contained'}
color={'limegreen'}
style={{
borderRadius: 5,
marginHorizontal: 20,
marginVertical: 10,
justifyContent: 'center',
}}
labelStyle={{ color: 'white', padding: 10 }}
uppercase={false}
onPress={() => { }}
disabled={totalPaymentAmount <= 0 ? true : false}>
<Text>Pay Bill</Text>
</Button>
</>
}
</>
);
}
class Account extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {
};
}
render() {
return (
<>
{<AccountMultiplePayment {...this.props} {...this.navigation} />}
</>
);
}
}
export default Account;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
headerTitle: {
alignItems: 'center',
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
paddingHorizontal: 20,
paddingVertical: 20,
},
subHeaderContainer: {
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: 'white',
borderRadius: 10,
elevation: 5,
marginVertical: 5,
marginHorizontal: 10,
},
subHeaderTitle: {
color: 'white',
fontWeight: 'bold',
fontSize: 16,
backgroundColor: '#2c1855',
padding: 10,
borderRadius: 10,
},
defaultText: {
color: 'black',
},
});
This is what it looks like currently. All checked items are individually selected:
This is what I am trying to achieve:
After checking the documentation of react-native-bouncy-checkbox, the following is important.
isCheckeddetermines the default internal checkbox state. It is only evaluated once.
The check-state handling is handled internally by the library.
We want to handle this on our own, in order to check all checkbox with a single state change. To do so, we need to set the disableBuiltInState prop to true "if you want to manually handle the isChecked prop and disable built in handling".
Thus, I would suggest the following workflow.
Create a state array for isChecked which contains a boolean flag for each of the elements.
Create a custom onPress-handler which gets the index from the FlatList as a parameter. With that index, we are able to to set the correct boolean flag in our state array to true or false.
If Select All is pressed, our onPress-handler sets all boolean flags of our state array to true. This will cause a rerendering of our screen and since we set disableBuiltInState prop to true, react-native-bouncy-checkbox will use our custom state handling.
Here is a minimal working example.
// set inititial all to false, you might want some different initial state
const [checks, setChecks] = useState(Array.from({ length: apiData.length }, () => false))
const onCheck = React.useCallback(
(index) => {
let previous = [...checks]
previous[index] = !previous[index]
setChecks(previous)
},
[checks, setChecks]
)
const selectAll = React.useCallback(() => {
let previous = [...checks]
setChecks(previous.map(() => true))
}, [checks, setChecks])
return (
<View style={{ padding: 40, marginTop: 50 }}>
<FlatList
data={apiData}
renderItem={({ item, index }) => (
<View style={{ padding: 20 }}>
<BouncyCheckbox
isChecked={checks[index]}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={() => onCheck(index)}
disableBuiltInState={true}
/>
</View>
)}
keyExtractor={(item) => "" + item.id}
removeClippedSubviews={false}
/>
<TouchableOpacity onPress={selectAll}>
<Text style={{ fontSize: 20, color: "red" }}> Select All</Text>
</TouchableOpacity>
</View>
)
This looks as follows.
Pressing Select All yields
Since you want to calculate additional values (total payable amount for each selected item) I would suggest to just add the checks state to your already implemented useEffect. When the checks state changes, this useEffect will be called. You can calculate all fields for which the boolean flag inside checks is true and set the state for the input fields.
Let refactor code as below
import "react-native-gesture-handler";
import React, { useState, useEffect, Component } from "react";
import {
StyleSheet,
View,
Text,
TouchableOpacity,
FlatList,
} from "react-native";
import { Button, Divider, TextInput } from "react-native-paper";
import BouncyCheckbox from "react-native-bouncy-checkbox";
function AccountMultiplePayment({ navigation }) {
const [apiData, setApiData] = useState([
{
id: 1,
name: "John",
address: "address 1",
amount: "79.90",
},
{
id: 2,
name: "Simon",
address: "address 2",
amount: "35.50",
},
{
id: 3,
name: "Tim",
address: "address 3",
amount: "15.50",
},
{
id: 4,
name: "Rob",
address: "address 4",
amount: "33.33",
},
{
id: 5,
name: "Sarah",
address: "address 5",
amount: "77.77",
},
]);
const [billPaymentAmount, setBillPaymentAmount] = useState({});
const [selectedBill, setSelectedBill] = useState([]);
const [totalPaymentAmount, setTotalPaymentAmount] = useState(0);
const computeBillPaymentAmount = () => {
let newBillPaymentAmount = {};
apiData.forEach(({ id, amount }) => {
newBillPaymentAmount[id] = amount;
});
return newBillPaymentAmount;
};
const computeTotalPaymentAmount = () => {
let total = 0;
selectedBill.forEach((id) => {
total += parseFloat(billPaymentAmount[id]);
});
// Prevent NaN issue, because once user delete amount will become empty string
return total ? total : 0;
};
useEffect(() => {
setBillPaymentAmount(computeBillPaymentAmount());
}, [selectedBill.length]);
useEffect(() => {
setTotalPaymentAmount(computeTotalPaymentAmount());
}, [billPaymentAmount]);
const selectAllBill = () => {
if (selectedBill.length < apiData.length) {
setSelectedBill([...new Set(apiData.map((item) => item.id))]);
}
if (selectedBill.length === apiData.length) {
setSelectedBill([]);
}
};
const isBillAdded = (id) => selectedBill.some((el) => el === id);
const hasAllBillselected = apiData.length === selectedBill.length;
return (
<>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity
style={{ alignItems: "center", paddingVertical: 10 }}
onPress={selectAllBill}
>
<Text style={{ color: "black", fontSize: 25 }}>Select All</Text>
</TouchableOpacity>
<BouncyCheckbox
disableBuiltInState
isChecked={hasAllBillselected}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={selectAllBill}
/>
</View>
<FlatList
data={apiData}
renderItem={({ item }) => {
return (
<View style={{ flexDirection: "row" }}>
<View style={[styles.subHeaderContainer, { flex: 1 }]}>
<Text
style={[
styles.defaultText,
{ fontWeight: "bold", fontSize: 16 },
]}
>
{item.name}
</Text>
<Divider style={{ marginVertical: 5 }} />
<View style={{ flexDirection: "row" }}>
<Text
style={[
styles.defaultText,
{ fontWeight: "bold", flex: 2 },
]}
>
Total Payable Amount:
</Text>
<View style={{ flex: 1 }}>
<TextInput
value={billPaymentAmount[item.id]}
onChangeText={(value) =>
setBillPaymentAmount({
...billPaymentAmount,
[item.id]: value,
})
}
keyboardType={"numeric"}
mode={"outlined"}
label={"RM"}
dense={true}
/>
</View>
</View>
</View>
<BouncyCheckbox
disableBuiltInState
isChecked={selectedBill.includes(item.id)}
fillColor={"green"}
unfillColor={"#FFFFFF"}
onPress={() => {
if (selectedBill.includes(item.id)) {
setSelectedBill(
selectedBill.filter((value) => value !== item.id)
);
} else {
setSelectedBill([...new Set([...selectedBill, item.id])]);
}
}}
/>
</View>
);
}}
keyExtractor={(item) => item.id}
removeClippedSubviews={false}
/>
{
<>
<View
style={{
paddingVertical: 10,
paddingHorizontal: 20,
flexDirection: "row",
backgroundColor: "blue",
}}
>
<Text
style={{
color: "white",
flex: 1,
fontWeight: "bold",
fontSize: 18,
}}
>
Total Amount:{" "}
</Text>
<View>
<Text
style={{ color: "white", fontWeight: "bold", fontSize: 24 }}
>
RM {totalPaymentAmount.toFixed(2)}
</Text>
{totalPaymentAmount <= 0 ? null : (
<TouchableOpacity
onPress={() => {
//navigation.goBack();
navigation.goBack();
navigation.navigate("Account");
}}
>
<Text>Reset</Text>
</TouchableOpacity>
)}
</View>
</View>
<Button
mode={"contained"}
color={"limegreen"}
style={{
borderRadius: 5,
marginHorizontal: 20,
marginVertical: 10,
justifyContent: "center",
}}
labelStyle={{ color: "white", padding: 10 }}
uppercase={false}
onPress={() => {}}
disabled={totalPaymentAmount <= 0 ? true : false}
>
<Text>Pay Bill</Text>
</Button>
</>
}
</>
);
}
class Account extends Component {
constructor(props) {
super(props);
this._isMounted = false;
this.state = {};
}
render() {
return (
<>{<AccountMultiplePayment {...this.props} {...this.navigation} />}</>
);
}
}
export default Account;
const styles = StyleSheet.create({
flex: {
flex: 1,
},
headerTitle: {
alignItems: "center",
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
paddingHorizontal: 20,
paddingVertical: 20,
},
subHeaderContainer: {
paddingVertical: 10,
paddingHorizontal: 20,
backgroundColor: "white",
borderRadius: 10,
elevation: 5,
marginVertical: 5,
marginHorizontal: 10,
},
subHeaderTitle: {
color: "white",
fontWeight: "bold",
fontSize: 16,
backgroundColor: "#2c1855",
padding: 10,
borderRadius: 10,
},
defaultText: {
color: "black",
},
});
Working example here - https://snack.expo.dev/#emmbyiringiro/971a0c
I am attempting to run a map on my app. This code works perfectly on iOS, but throws the above error on Android alone. I have tried troubleshooting with current online resources, but they are either older, or they are mainly for "class" format, while my code is written using hooks.
The full error is:
Error using newLatLngBounds (LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view. Either eait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions.
Below is my code,
import React, { useState, useEffect, Component }from "react";
import {ActivityIndicator, Alert, BackHandler, Button, Dimensions, Platform, SafeAreaView, StyleSheet, Text, TouchableOpacity, ScrollView, View,} from "react-native";
import MapView, { Marker, Callout, Polyline, PROVIDER_GOOGLE, Heatmap } from "react-native-maps";
import Modal from 'react-native-modal';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import * as Localization from 'expo-localization'
import { useFocusEffect } from '#react-navigation/native'
import { Permissions , Request } from 'expo-permissions'
import * as Location from 'expo-location'
function Locator({navigation, route}) {
const [user_latitude, setUserLatitude] = useState(0)
const [user_longitude, setUserLongitude] = useState(0)
const [position_error, setPositionError] = useState(null)
const [ isLoading, setIsLoading ] = useState(true)
const [ location, setLocation ] = useState(null);
const renderLoading = () => {
if (isLoading) {
return (
<View style = {{justifyContent: 'center', backgroundColor: '#d3d3d3', height: height, width: width}}>
<ActivityIndicator
color = 'black'
size = 'large'
animated = {false}
/>
<Text style = { styles.text }>Locating</Text>
</View>
);
}else {
return null;
}
}
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', () => true)
return () =>
BackHandler.removeEventListener('hardwareBackPress', () => true)
},
[]
)
useFocusEffect(
React.useCallback(()=> {
let isActive = true;
const fetchGeoPosition = () => {
navigator.geolocation.getCurrentPosition(
position => {
if (isActive){
setUserLatitude(position.coords.latitude);
setUserLongitude(position.coords.longitude);
setPositionError(null);
console.log('Location Accessed')
}
setIsLoading(false)
},
error => isActive && setPositionError(error.message),
{enableHighAccuracy: true, timeout: 0, maximumAge: 1000}
);
}
const permission_get = async () => {
if (Platform.OS === 'android' && !Constants.isDevice) {
setErrorMsg(
'Oops, this will not work on Snack in an Android emulator. Try it on your device!'
);
return;
}
let { status } = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
console.log('Not permitted')
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
}
permission_get()
fetchGeoPosition()
return () =>{
isActive = false
console.log('Location Severed')
}
},
[],
),
)
return(
<View style = {styles.container}>
{(renderLoading())}
<View style = {styles.header}>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
region= {{
latitude: user_latitude,
longitude: user_longitude,
latitudeDelta: 0.1451,
longitudeDelta: 0.1451
}}>
<Marker
coordinate={
{latitude: user_latitude,
longitude: user_longitude,
error: position_error,
}}
>
</Marker>
</MapView>
</View>
<View style = {styles.footer}>
<View style = {styles.buttonView}>
<TouchableOpacity
onPress= { null }
style = {styles.signIn}
>
<Text style = {styles.buttonText}>Cancel</Text>
</TouchableOpacity>
</View>
<View style = {styles.buttonView}>
<TouchableOpacity
onPress = { null }
style = {styles.signIn}
>
<Text style = {styles.buttonText}>Continue</Text>
</TouchableOpacity>
</View>
</View>
</View>
)
}
export {Locator};
const {height}= Dimensions.get("screen");
const { width } = Dimensions.get("screen")
const styles = StyleSheet.create({
container: {
flex: 100,
flexDirection: 'column',
},
header:{
flex: 90,
width: width,
},
map: {
...StyleSheet.absoluteFillObject,
},
middle: {
width: '100%',
flex: 15,
borderColor: '#d3d3d3',
borderWidth: 5,
backgroundColor: '#d3d3d3',
},
footer: {
width: width,
flex: 10,
backgroundColor: '#d3d3d3',
justifyContent: 'space-evenly',
flexDirection: 'row',
},
text: {
textAlign: 'center',
color: 'black',
},
button: {
fontSize: height*0.03,
width: width*0.45,
textAlign: 'center',
height: height*0.05,
color: '#d3d3d3',
fontWeight: 'bold',
borderRadius: 20,
justifyContent: 'center'
},
buttonText:{
textAlign: 'center',
fontWeight: 'bold',
color: 'black',
fontSize: height*0.025
},
textSign: {
color: '#fff',
fontWeight: 'bold',
fontSize: 20,
width: '100%',
paddingTop: 20,
textAlign: 'center',
},
modal: {
backgroundColor: '#d3d3d3',
alignItems: 'center',
width: '100%',
height: '20%',
paddingRight: 20,
borderRadius: 10,
color: 'black',
},
searchBox:{
top: 0,
position: 'absolute',
width: width,
},
signIn: {
width: width*0.45,
height: height*0.055,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 50,
flexDirection: 'row',
fontSize: height*0.02,
borderColor: 'black',
backgroundColor: '#fff',
marginTop: height*0.01,
},
buttonView:{
alignItems: 'center',
marginTop: height*0.001,
},
});
While
map: {
...StyleSheet.absoluteFillObject,
},
is an acceptable format for iOS, Android requires you to specify the Map's dimensions. The error above can be fixed by doing something as simple as:
map: {
...StyleSheet.absoluteFillObject,
height: height*0.8
},
i need your helps.
I have a functional based component call WelcomeSlidePage. I want to pass a functional to the functional component. i try to console.log the function in functional component, in the first state, console.log is printing the function, but after the second state, it's becomes undefine.
this is my component that calling the functional component
WebViewPage.jsx
<Modal transparent={true} visible={this.state.WelcomeSlidePageModal} animationType="
<WelcomeSlidePage onDone={()=>{console.log('test bro');this.setState({WelcomeSlidePageModal:false})}}/>
</Modal>
and this is my functional component
WelcomeSlidePage.jsx
import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
import string from '../string'
import { _storeData, _getData } from '../components/StoreAsync';
export default WelcomeSlide = (props) => {
const [language, setLanguage] = useState('english');
const [showRealApp, setShowRealApp] = useState(false);
const welcomeSlide = string.welcome_slide[language]
useEffect(() => {
console.log('testing bro',props.onDone)
// getData();
});
async function getData() {
setLanguage( await _getData("language"));
setShowRealApp( await _getData("showRealApp"));
}
_renderItem = ({ item }) => {
switch (item.key) {
case ('k4'):
return (
<View style={{backgroundColor : '#a0c83a', flex: 1}}>
<Text style={styles.title}>{item.title}</Text>
<View style={{justifyContent: 'center', paddingHorizontal: 20, flex: 1}}>
<Text style={styles.text}>{item.text_4_a}</Text>
<View style={{flexDirection: 'row'}}>
<Image style={styles.icon} source={item.icon} />
<Text style={{paddingStart: 5, paddingEnd: 20, ...styles.text}}>{item.text_4_b}</Text>
</View>
<Text style={styles.text}>{item.text_4_c}</Text>
<View style={{flexDirection: 'row'}}>
<Image style={styles.icon} source={item.icon} />
<Text style={{paddingStart: 5, paddingEnd: 20, ...styles.text}}>{item.text_4_d}</Text>
</View>
</View>
</View>
);
case ('k5'):
return (
<View style={styles.slide}>
<Text style={styles.title}>{item.text_5}</Text>
<TouchableOpacity style={{marginVertical: 24}} onPress={()=>{ props.navigation.navigate('WebView', { url: string.onboarding[language].login_url }); }}>
<Text style={styles.button}>{item.text_5_btn1}</Text>
</TouchableOpacity>
<TouchableOpacity style={{marginVertical: 24}} onPress={()=>{ props.navigation.navigate('WebView', { url: string.onboarding[language].register_url }); }}>
<Text style={styles.button}>{item.text_5_btn2}</Text>
</TouchableOpacity>
</View>
);
default:
return (
<View style={styles.slide}>
<Text style={styles.title}>{item.title}</Text>
<Image style={styles.image} source={item.image} />
<Text style={{paddingHorizontal: 20, ...styles.text}}>{item.text}</Text>
</View>
);
}
}
_onDone = () => {
setShowRealApp(true);
_storeData('isPassSlide',true);
props.navigation.navigate('WebView', { url: string.onboarding[language].login_url,splashScreen:false });
}
const slides = [
{
key: 'k1',
title: welcomeSlide.title,
text: welcomeSlide.a,
image: require('../images/my_library_card_white_notext_nopadding.png'),
backgroundColor: '#a0c83a',
},
{
key: 'k2',
title: welcomeSlide.title,
text: welcomeSlide.b,
image: require('../images/my_account_white_notext_nopadding.png'),
backgroundColor: '#a0c83a',
},
{
key: 'k3',
title: welcomeSlide.title,
text: welcomeSlide.c,
image: require('../images/library_catalog_white_notext_nopadding.png'),
backgroundColor:'#a0c83a',
},
{
key: 'k4',
title: welcomeSlide.title,
text_4_a: welcomeSlide.d_a,
text_4_b: welcomeSlide.d_b,
text_4_c: welcomeSlide.d_c,
text_4_d: welcomeSlide.d_d,
icon: require('../images/icon-hand-right.png'),
backgroundColor: '#a0c83a',
},
{
key: 'k5',
text_5: welcomeSlide.e,
text_5_btn1: welcomeSlide.e_btn1,
text_5_btn2: welcomeSlide.e_btn2,
backgroundColor:'#a0c83a',
},
];
return(
<AppIntroSlider
renderItem={_renderItem}
prevLabel={string.back[language]}
nextLabel={string.next[language]}
doneLabel={string.next[language]}
showPrevButton={true}
slides={slides}
onDone={()=>props.onDone()}/>
)
}
const styles = StyleSheet.create({
slide : {
flex: 1,
paddingTop: (Platform.OS) === 'ios' ? 20 : 0,
paddingBottom: 80,
backgroundColor : '#a0c83a',
alignItems: 'center',
},
title: {
fontSize: 24,
color: '#fff',
fontWeight: 'bold',
textAlign: 'center',
marginTop : 30,
},
text: {
fontSize : 20,
color: '#fff',
textAlign: 'left',
},
image: {
width: 200,
height: 200,
resizeMode: 'contain',
flex:1,
},
icon: {
top: 10,
width: 25,
height: 15,
width: 25,
marginTop: -3,
},
content: {
paddingHorizontal: 20,
},
button: {
borderRadius: 8,
paddingVertical: 12,
paddingHorizontal: 12,
color: 'white',
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginHorizontal: 40,
backgroundColor: '#e46825',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.8,
shadowRadius: 2,
elevation: 5,
},
});
and then this is the console log
testing bro function onDone() {
console.log('test bro');
_this2.setState({
WelcomeSlidePageModal: false
});
} b11b05fa-4e76-41da-9d58-218edc178e45:157683:15
testing bro undefined
please help me guys, thanks
I am getting undefined is not an object evaluating _this.props.navigation. Here is my code.
I want to use the in multiple screens so I have to
extract it out and call it in any I need it in.
I have tried https://github.com/react-navigation/react-navigation/issues/2198#issuecomment-316883535 to no luck.
Category.js
import React, {Component} from 'react';
import {View, FlatList} from 'react-native';
import {ListItem} from 'react-native-elements'
import {AppRegistry, TouchableOpacity, ActivityIndicator} from 'react-native';
import {SearchHeader} from '../SearchHeader';
export default class Category extends Component {
constructor() {
super();
this.state = {
list: [],
};
this.onPress = this.onPress.bind(this);
}
static navigationOptions = {
title: 'Categories',
headerStyle: {backgroundColor: '#ffb30c'},
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "98%",
backgroundColor: "#CED0CE",
marginLeft: "1%",
marginRight: "1%"
}}
/>
);
};
_keyExtractor = (item, index) => item.name;
renderHeader = () => {
return (<SearchHeader />);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large"/>
</View>
);
};
onPress = (item) => {
this.props.navigation.navigate('SpecTypeScreen',{cat:item});
};
search = () => {
};
render() {
return (
<FlatList
data={this.state.list}
renderItem={({item}) => (
<TouchableOpacity onPress={() => this.onPress(item)}>
<ListItem
title={`${item.name}`}
containerStyle={{borderBottomWidth: 0}}
/>
</TouchableOpacity>
)}
keyExtractor={this._keyExtractor}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
/>
);
}
}
AppRegistry.registerComponent('CategoryScreen', () => CategoryScreen);
SearchHeader.js
import React, {Component} from 'react';
import Autocomplete from 'react-native-autocomplete-input';
import {
AppRegistry,
View,
StyleSheet,
Platform,
Text,
TouchableOpacity,
} from 'react-native';
import {withNavigation} from 'react-navigation';
import colors from './config/colors';
import normalize from './config/normalizeText';
export class SearchHeader extends Component {
constructor() {
super();
this.state = {
list: [],
};
}
search = (term) => {
if (term.length > 2) {
fetch("https://myUrl?term=" + encodeURI(term))
.then((response) => response.json())
.then((responseJson) => {
this.setState({list: responseJson});
console.log(responseJson);
})
.catch((error) => {
console.error(error)
});
}
else{
this.setState({list: []});
}
};
onPress = (item) => {
this.props.navigation.navigate('ProductScreen',{spec:item});
};
render() {
return (
<View style={[
styles.container, styles.containerLight
]}>
<Autocomplete placeholder="Search Specs & Approvals..."
autoCorrect={false}
onChangeText={this.search}
data={this.state.list}
containerStyle={{backgroundColor: "#d71201"}}
inputStyle={{backgroundColor: "#fff"}}
renderItem={({ id, specification }) => (
<TouchableOpacity style={styles.autocompleteContainer} onPress={this.onPress.bind(this, specification)}>
<Text style={styles.itemText}>
{specification}
</Text>
<View
style={{
height: 1,
width: "98%",
backgroundColor: "#CED0CE",
marginLeft: "1%",
marginRight: "1%"
}}
/>
</TouchableOpacity>
)}
style={[
styles.input,
styles.inputLight,
{borderRadius: Platform.OS === 'ios' ? 15 : 20},
{paddingRight: 50}
]}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
borderTopWidth: 1,
borderBottomWidth: 1,
borderBottomColor: '#000',
borderTopColor: '#000',
backgroundColor: "#d71201",
maxHeight:70
},
containerLight: {
borderTopColor: '#e1e1e1',
borderBottomColor: '#e1e1e1',
},
input: {
paddingLeft: 26,
paddingRight: 19,
margin: 8,
borderRadius: 3,
overflow: 'hidden',
backgroundColor: colors.grey5,
fontSize: normalize(14),
color: colors.grey3,
height: 40,
...Platform.select({
ios: {
height: 30,
},
android: {
borderWidth: 0,
},
}),
},
inputLight: {
backgroundColor: "#fff"
},
autocompleteContainer: {
backgroundColor:"#fff",
marginLeft: 10,
marginRight: 10
},
itemText: {
fontSize: 15,
margin: 5,
marginLeft: 20,
paddingTop:5,
paddingBottom:5
},
descriptionContainer: {
backgroundColor: '#F5FCFF',
marginTop: 8
},
infoText: {
textAlign: 'center'
},
titleText: {
fontSize: 18,
fontWeight: '500',
marginBottom: 10,
marginTop: 10,
textAlign: 'center'
},
directorText: {
color: 'grey',
fontSize: 12,
marginBottom: 10,
textAlign: 'center'
},
openingText: {
textAlign: 'center'
}
});
AppRegistry.registerComponent('SearchHeader', () => SearchHeader);
You need to pass the navigation prop down. Try this:
renderHeader = () => {
return (<SearchHeader navigation={this.props.navigation} />);
};
You need to wrap your component with withNavigation(Component)
Example: AppRegistry.registerComponent('SearchHeader', () => withNavigation(SearchHeader));
The point of withNavigation was so that you wouldn't need to pass navigation as a prop. Especially useful when you're passing through multiple children.