I am relatively new to React-Native and mobile app development. I am trying to create a clone of Instagram. And in the post screen what I do is I have a button Camera and once I click on it, that should take me to CameraView screen. And in the CameraView screen I will take a picture using expo-camera and save the picture in a variable. So here, I want to export the picture and then import it in my Post screen, so that the user can have a preview of what the picture looks like before uploading. And another thing to note is that, the Post screen will by default have a Placeholder image and once the image is taken the placeholder image has to be replaced with the image.
I tried this code but it didnt work.
My Post screen :-
import { View, Text, Image, TextInput } from 'react-native';
import React, { useState, useEffect } from 'react'
import * as Yup from 'yup';
import { Formik } from 'formik'
import { Button, Divider } from 'react-native-elements';
import { useNavigation } from '#react-navigation/native';
import CameraView, { takePicture } from '../../screens/CameraView';
const PLACEHOLDER_IMG = require('../../assets/defImg.png')
const uploadPostSchema = Yup.object().shape({
caption: Yup.string().max(2200, 'Caption cannot exceed character limit.')
})
const FormikPostUploader = ({route}) => {
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const navigation = useNavigation()
const handleCameraPress = () => {
UserImage = takePicture()
setThumbnailUrl(UserImage)
navigation.navigate('CameraView')
}
return (
<Formik
initialValues={{ imageUrl: '', caption:'' }}
onSubmit={(values) => console.log(values)}
validationSchema={uploadPostSchema}
validateOnMount={true}>
{({
handleBlur,
handleChange,
handleSubmit,
values,
errors,
isvalid
}) => (
<>
<View style={{ margin: 20, justifyContent: 'space-between', flexDirection: 'row' }}>
<Image source={thumbnailUrl} />
<View style={{ flex: 1, margin: 15 }}>
<TextInput
placeholder='Write a caption...'
placeholderTextColor='gray'
multiline={true}
style={{ color: 'white', fontSize: 17 }}
onChangeText={handleChange('caption')}
onBlur={handleBlur('caption')}
value={values.caption}
/>
</View>
</View>
<Divider width={0.5} orientation='vertical'/>
<View style={{ alignItems: 'center', marginTop: 15 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', color: 'white' }}>Choose Image</Text>
</View>
<View style={{ justifyContent: 'space-between', flexDirection: 'column', margin: 20 }}>
<View style={{ justifyContent: 'space-between', flexDirection: 'row', margin: 30 }}>
<Button title='Camera' onPress={handleCameraPress}/>
<Button title='Gallery' onPress={() => navigation.navigate('GalleryView')} />
</View>
<Button onPress={handleSubmit} title='Post' disabled={isvalid} />
</View>
</>
)}
</Formik>
)
}
export default FormikPostUploader
And my CameraView screen :-
import { View, Text, Image, StyleSheet, Button } from 'react-native'
import React, {useState, useEffect} from 'react'
import { Camera } from 'expo-camera'
import { TouchableOpacity } from 'react-native'
import { useNavigation } from '#react-navigation/native'
import { PLACEHOLDER_IMG } from '../components/newPost/FormikPostUploader'
export const takePicture = async(camera) => {
const data = await camera.takePictureAsync(null)
return data.uri
}
const Header = () =>{
const navigation = useNavigation()
return (
<View style = {styles.headerContainer}>
<TouchableOpacity style = {{marginBottom:15}} onPress={() => navigation.goBack()}>
<Image source = {require('../assets/back.png')} style = {{width:35, height:35, margin:20}}/>
</TouchableOpacity>
<Text style={{color:'white', fontWeight: '700', fontSize:20, marginRight: 25, marginTop:35, marginBottom:15}}>New Post</Text>
<Text> </Text>
</View>
)
}
const CameraView = () => {
const UserImage = takePicture()
const navigation = useNavigation()
const [hasCameraPermission, setHasCameraPermission] = useState(null)
const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
const [camera, setCamera] = useState(null)
const [type, setType] = useState(Camera.Constants.Type.back)
const captureImage = async () => {
if (camera) {
const UserImage = await takePicture(camera)
setThumbnailUrl(UserImage)
}
}
useEffect(() => {
(async() => {
const cameraStatus = await Camera.requestCameraPermissionsAsync()
setHasCameraPermission(cameraStatus.status === 'granted')})()},
[])
if (hasCameraPermission === null) {
return <View/>
}
if (hasCameraPermission === false){
return <Text>Enable access for Camera to proceed</Text>
}
return (
<View style={{ flex: 1, backgroundColor: 'black'}}>
<Header/>
<Camera
ref = {ref => setCamera(ref)}
style={styles.fixedRatio}
type={type}
ratio={'1:1'}/>
<View style = {styles.cameraContainer}>
<TouchableOpacity onPress={() => {setType(type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back)}}>
<Image source={require('../assets/turn-camera.png')} style = {{width:70, height:70, margin: 15}}/>
</TouchableOpacity>
<TouchableOpacity onPress={() => {captureImage(), navigation.goBack(), console.log(UserImage)}}>
<Image source={require('../assets/camera-shutter.png')} style = {{width:70, height:70, marginRight:60}}/>
</TouchableOpacity>
</View>
</View>
)
}
const styles = StyleSheet.create({
cameraContainer: {
flex:0.15,
backgroundColor: 'black',
flexDirection:'row',
justifyContent: 'space-between',
alignItems: 'center',
},
fixedRatio: {
flex:0.99,
aspectRatio:0.9
},
headerContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor:'black'
},
icon: {
width:25,
height: 25,
marginHorizontal:15,
marginTop:30,
},
iconContainer: {
flexDirection:'row',
justifyContent: 'space-between',
height:50,
},
}
)
export default CameraView
Am I doing anything wrong. Can you help me fix this. Thanks in advance!
Related
Data listing process with Firebase(Firestore) in React Native.
In the video I watched, he wrote and ran the code that lists the coins belonging to the user using firestore. It did not work even though I wrote the same codes. What is the reason? the code does not give an error, the screen appears, but it does not list.
The problem is that when I did the listing in console it worked. However, when I try to list with FlatList, the screen appears blank.
import { View, Text, SafeAreaView, TouchableOpacity, FlatList } from 'react-native'
import React, { useContext, useState, useEffect } from 'react'
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
import IconFA from 'react-native-vector-icons/FontAwesome'
import { deviceWidth, deviceHeight } from '../../utils/dimensions'
import { AuthContext } from '../../navigation/AuthProvider'
import { Formik, validateYupSchema } from 'formik'
import * as yup from 'yup'
import { firebase } from '#react-native-firebase/auth'
import auth from '#react-native-firebase/auth'
import firestore from '#react-native-firebase/firestore'
const color = '#aaa';
const HomeScreen = ({ navigation }) => {
const renderItem = (item) => {
<TouchableOpacity
key={item.id}
style={{
flexDirection: 'row',
width: '95%',
height: 60,
borderWidth: 1,
margin: 10,
borderRadius: 20,
padding: 10,
justifyContent: 'space-between',
alignItems: 'center'
}}>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'left',
fontSize: 18
}}>{item.coinID}</Text>
</View>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'right',
fontSize: 18
}}>{item.value}</Text>
</View>
</TouchableOpacity>
}
const { signOut, user } = useContext(AuthContext);
const [currentUser, setCurrentUser] = useState({});
const [userCoinList, setUserCoinList] = useState([]);
const usersColl = firestore().collection('users');
const coinsColl = firestore().collection('coins');
const userCoinsColl = firestore().collection('userCoins');
useEffect(() => {
return usersColl
.doc(user.uid)
.get()
.then(result => {
setCurrentUser(result.data());
userCoinsColl.onSnapshot((querySnapshot) => {
let list = [];
querySnapshot.forEach(doc => {
const { userID, coinID, value } = doc.data();
if (userID == user.uid) {
list.push({
id: doc.id,
userID,
coinID,
value
});
setUserCoinList(list);
}
});
});
});
}, []);
return (
<SafeAreaView style={{ width: '100%', height: '100%' }}>
<View style={{
width: '100%',
height: '90%',
padding: 5,
flex: 1,
alignItems: 'center'
}}>
<View>
<FlatList
style={{ flex: 3, backgroundColor: '#f00' }}
data={userCoinList}
keyExtractor={item => item.id}
renderItem={renderItem}
/>
{console.log(userCoinList)}
</View>
</View>
</SafeAreaView>
)
}
export default HomeScreen
Your renderItem function does not return anything.
Here is a fix.
const renderItem = (item) => {
return <TouchableOpacity
key={item.id}
style={{
flexDirection: 'row',
width: '95%',
height: 60,
borderWidth: 1,
margin: 10,
borderRadius: 20,
padding: 10,
justifyContent: 'space-between',
alignItems: 'center'
}}>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'left',
fontSize: 18
}}>{item.coinID}</Text>
</View>
<View style={{ flex: 1 }}>
<Text style={{
textAlign: 'right',
fontSize: 18
}}>{item.value}</Text>
</View>
</TouchableOpacity>
}
I try to create a modal on a login screen but i have a weird issue. When i click on the button for open the modal it open as well and i can close it too. But when i want to Re-open the modal i have to click 2 times on the button for it open again.
Here my code:
Login screen:
import React, { useState } from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
import { LinearGradient } from "expo-linear-gradient";
import { globalStyles } from "../styles/globalStyles";
import { Ionicons } from "#expo/vector-icons";
import LogModal from "../components/LogModal";
const Login = () => {
const [openToggle, setOpenToggle] = useState(false);
const handleToggle = () => {
setOpenToggle((prev) => !prev);
};
return (
<LinearGradient colors={["#000", "#fff"]} style={globalStyles.container}>
<View style={globalStyles.titleContainer}>
<Text style={globalStyles.title}>HGDZ</Text>
</View>
<View style={globalStyles.logo}>
<Ionicons name="md-ice-cream-outline" size={80} color="white" />
</View>
<View style={globalStyles.btnLogContainer}>
<TouchableOpacity style={globalStyles.touchable} onPress={handleToggle}>
<View style={globalStyles.btnContainer}>
<Text style={globalStyles.btnLogText}>S'incrire</Text>
</View>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.touchable} onPress={handleToggle}>
<View style={globalStyles.btnContainer}>
<Text style={globalStyles.btnLogText}>Connexion</Text>
</View>
</TouchableOpacity>
</View>
<LogModal open={openToggle} />
</LinearGradient>
);
};
export default Login;
Modal component:
import React, { useState, useLayoutEffect } from "react";
import { StyleSheet, Text, View, Modal, Pressable, Alert } from "react-native";
import { globalStyles } from "../styles/globalStyles";
const LogModal = (props) => {
const [modalVisible, setModalVisible] = useState(false);
useLayoutEffect(() => {
setModalVisible(props.open);
}, [props]);
const handleModal = () => {
setModalVisible((prev) => !prev);
console.log(modalVisible);
};
return (
<View style={globalStyles.centeredView}>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
setModalVisible(!modalVisible);
}}
>
<View style={globalStyles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>Hello World!</Text>
<Pressable
style={[styles.button, styles.buttonClose]}
onPress={() => setModalVisible((prev) => !prev)}
>
<Text style={styles.textStyle}>Hide Modal</Text>
</Pressable>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22,
},
modalView: {
marginTop: "50%",
margin: 20,
backgroundColor: "white",
borderRadius: 20,
padding: 35,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5,
},
button: {
borderRadius: 20,
padding: 10,
elevation: 2,
},
buttonOpen: {
backgroundColor: "#F194FF",
},
buttonClose: {
backgroundColor: "#2196F3",
},
textStyle: {
color: "white",
fontWeight: "bold",
textAlign: "center",
},
modalText: {
marginBottom: 15,
textAlign: "center",
},
});
export default LogModal;
I don't see where i made mistake an y advice?
Srry if the title makes no sense. Don't know a better title.
How can I save route.params items that I pass to my second screen using AsyncStorage?
In my first screen i have a bunch of data in a FlatList that can be opened with a Modal. Inside that Modal I have a TouchableOpacity that can send the data thats inside the Modal to my second screen. The data that has been passed to the second screen is passed to a FlatList. The data in the FlatList should be saved to AsyncStorage. Tried alot of things getting this to work, but only getting warning message
undefined. Code below is the most recent progress.
Using React Navigation V5.
FIRST SCREEN
const [masterDataSource, setMasterDataSource] = useState(DataBase);
const [details, setDetails] = useState('');
<TouchableOpacity
onPress={() => {
const updated = [...masterDataSource];
updated.find(
(item) => item.id === details.id,
).selected = true;
setMasterDataSource(updated);
navigation.navigate('cart', {
screen: 'cart',
params: {
items: updated.filter((item) => item.selected),
},
});
setModalVisible(false);
}}>
<Text>Add to cart</Text>
</TouchableOpacity>
SECOND SCREEN
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { useTheme } from '../Data/ThemeContext';
import AsyncStorage from '#react-native-async-storage/async-storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
export default function ShoppingList({ route, navigation }) {
const [shoppingList, setShoppingList] = useState([]);
const { colors } = useTheme();
const todo = () => {
alert('Todo');
};
useEffect(() => {
restoreShoppingListAsync();
}, []);
const shoppingListAsync = () => {
const shoppingList = route.params && route.params.items;
setShoppingList(list);
storeShoppingList(list);
};
const asyncStorageKey = '#ShoppingList';
const storeShoppingListAsync = (list) => {
const stringifiedList = JSON.stringify(list);
AsyncStorage.setItem(asyncStorageKey, stringifiedList).catch((err) => {
console.warn(err);
});
};
const restoreShoppingListAsync = () => {
AsyncStorage.getItem(asyncStorageKey)
.then((stringifiedList) => {
console.log(stringifiedList);
const parsedShoppingList = JSON.parse(stringifiedList);
if (!parsedShoppingList || typeof parsedShoppingList !== 'object')
return;
setShoppingList(parsedShoppingList);
})
.then((err) => {
console.warn(err);
});
};
const RenderItem = ({ item }) => {
return (
<View>
<TouchableOpacity
style={{
marginLeft: 20,
marginRight: 20,
elevation: 3,
backgroundColor: colors.card,
borderRadius: 10,
}}>
<View style={{ margin: 10 }}>
<Text style={{ color: colors.text, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: colors.text }}>{item.gluten}</Text>
<Text style={{ color: colors.text }}>{item.id}</Text>
</View>
</TouchableOpacity>
</View>
);
};
const emptyComponent = () => {
return (
<View style={{ alignItems: 'center' }}>
<Text style={{ color: colors.text }}>Listan är tom</Text>
</View>
);
};
const itemSeparatorComponent = () => {
return (
<View
style={{
margin: 3,
}}></View>
);
};
return (
<View
style={{
flex: 1,
}}>
<View
style={{
padding: 30,
backgroundColor: colors.Textinput,
elevation: 12,
}}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={25} color="#fff" />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20 }}>Inköpslista</Text>
<TouchableOpacity>
<Ionicons
name="trash-outline"
size={25}
color="#fff"
onPress={() => todo()}
/>
</TouchableOpacity>
</View>
</View>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
data={shoppingList}
renderItem={RenderItem}
ListEmptyComponent={emptyComponent}
ItemSeparatorComponent={itemSeparatorComponent}
initialNumToRender={4}
maxToRenderPerBatch={5}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
showsVerticalScrollIndicator={true}
contentContainerStyle={{ paddingBottom: 20 }}
/>
</View>
</View>
);
}
As you are using async storage to maintain the cart.
I would suggest an approach as below
Update the asyn storage when new items are added to or removed from the cart
Retrieve the items from the cart screen and show the items there
Before you navigate store the items like below
AsyncStorage.setItem(
'Items',
JSON.stringify(updated.filter((item) => item.selected))
).then(() => {
navigation.navigate('Cart', {
items: updated.filter((item) => item.selected),
});
});
The cart screen would be something like below
function Cart({ navigation, route }) {
const [data,setData]=useState([]);
React.useEffect(() => {
async function fetchMyAPI() {
const value = await AsyncStorage.getItem('Items');
setData(JSON.parse(value));
}
fetchMyAPI();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button title="Go back" onPress={() => navigation.goBack()} />
<FlatList
data={data}
renderItem={RenderItem}
/>
</View>
);
}
Working Example
https://snack.expo.io/#guruparan/cartexample
1
I am a beginner and still learning to react native.
In my react native App, I have 2 screens. In the first page, I have JSON data ; I want to pass this JSON data to the next page.
I used react-navigation for navigating between pages. I need to passed each parameter for a new book screen for each book.
But I couldn't figure out, how to pass JSON data to next page! In BookScreen.js the function "getParam" is not been seen.
First Screen: ExploreScreen.js
import React, { useState, useEffect } from "react";
import {
View,
Text,
StyleSheet,
FlatList,
Image,
TouchableOpacity,
} from "react-native";
export default function ExploreScreen({ navigation, route }) {
const [data, setData] = useState([]);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
await fetch(
"http://www.json-generator.com/api/json/get/bTvNJudCPS?indent=2"
)
.then((response) => response.json())
.then((receivedData) => setData(receivedData));
};
return (
<View style={styles.container}>
<FlatList
data={data}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.book}
onPress={() => navigation.navigate("Book", item)}
>
<Image
style={styles.bookImage}
source={{ uri: item.book_image }}
></Image>
<View>
<Text style={styles.bookTitleText}>{item.title}</Text>
<Text style={styles.bookAuthor}>{item.author}</Text>
<Text style={styles.bookGenre}>
<Text styles={styles.gen}>Genul: </Text>
{item.genre_id}
</Text>
</View>
</TouchableOpacity>
)}
></FlatList>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
alignSelf: "center",
paddingVertical: "15%",
},
book: {
flex: 1,
flexDirection: "row",
marginBottom: 3,
},
bookImage: {
width: 100,
height: 100,
margin: 5,
},
bookTitleText: {
color: "#8B0000",
fontSize: 15,
fontStyle: "italic",
fontWeight: "bold",
},
bookAuthor: {
color: "#F41313",
},
});
Second Screen: BookScreen.js
import React from "react";
import { View, Text, StyleSheet } from "react-native";
export default function BookScreen({ navigation, route }) {
const { item } = route.params;
return (
<View style={styles.container}>
<Text style={styles.text}>{navigation.getParam("name")}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
alignSelf: "center",
paddingVertical: "100%",
},
text: {
fontSize: 20,
},
});
In your BookScreen, change it to the following:
export default function BookScreen({ navigation, route }) {
const { item } = route.params;
return (
<View style={styles.container}>
<Text style={styles.text}>{item.name}</Text>
</View>
);
}
Edit:
I think you should pass the data like this:
navigation.navigate('Book', {item: item});
I have Created Project with expo-camera which on click of button opens camera. However first time when i click that button only black screen is render, after reopening the screen, and clicking the same button, the Camera UI is render on Screen.
Problem
I don't know why it show black screen first. The Problem only exist in android devices.
Link to Expo Snack Try this example on android devices.
import React, { useState, useEffect, useContext } from 'react';
import {
SafeAreaView,
View,
Text,
Dimensions,
ImageBackground,
Image,
Alert,
} from 'react-native';
import { Camera } from 'expo-camera';
import { Entypo, Ionicons, FontAwesome } from '#expo/vector-icons';
import { Surface, Button, TextInput } from 'react-native-paper';
const { width, height } = Dimensions.get('window');
let cameraRef;
const PickImage = ({ navigation }) => {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const [isCameraUiOn, setIsCameraUiOn] = useState(false);
const [isCapturing, setIsCapturing] = useState(false);
const [flashMode, setFlashMode] = useState(true);
const [capturePhoto, setCapturePhoto] = useState(null);
const snap = async () => {
if (cameraRef) {
let photo = await cameraRef.takePictureAsync({
quality: 0.5,
});
setCapturePhoto(photo.uri);
setIsCapturing(false);
setIsCameraUiOn(false);
}
};
const getCameraPermission = async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
};
useEffect(() => {
getCameraPermission();
}, []);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
if (isCameraUiOn) {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }}
type={type}
ref={(ref) => (cameraRef = ref)}
flashMode={
flashMode
? Camera.Constants.FlashMode.on
: Camera.Constants.FlashMode.off
}>
<Entypo
name="cross"
color="#FFF"
size={50}
onPress={() => setIsCameraUiOn(false)}
/>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
display: 'flex',
justifyContent: 'space-evenly',
alignItems: 'flex-end',
flexDirection: 'row',
zIndex: 9999,
}}>
<Ionicons
name="md-reverse-camera"
color="#FFF"
size={35}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}
/>
{isCapturing ? (
<FontAwesome name="circle" color="#FFF" size={60} />
) : (
<FontAwesome
name="circle-o"
color="#FFF"
size={60}
onPress={() => {
setIsCapturing(true);
snap();
}}
/>
)}
<Ionicons
name="ios-flash"
color={flashMode ? 'gold' : '#FFF'}
size={35}
onPress={() => setFlashMode(!flashMode)}
/>
</View>
</Camera>
</View>
</SafeAreaView>
);
} else {
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ margin: 20 }}>
<Text
style={{
fontSize: 25,
fontWeight: '600',
}}>
Report Cattles with Ease !!
</Text>
<Text
style={{
marginVertical: 10,
}}>
Our System uses, cloud based fancy image detection algorithm to
process your image.
</Text>
</View>
<View
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}>
<Button
style={{
width: width * 0.5,
}}
icon="camera"
mode="contained"
onPress={() => setIsCameraUiOn(true)}>
Press me
</Button>
</View>
</SafeAreaView>
);
}
};
export default PickImage;
Link to snack Expo
im with the same problem rn, some people suggested asking for permission before rendering