React-Native expo modal with textinput, Keyboard and modal flicker after every text entry unable to input text - javascript

I have an issue with modal not being stable when text input receives entry. I have struggled with this and read most post on this site but when I try it, no results. One of them is Keyboard closes after every keypress in TextInput react native. I have taken most of the irrelevant code out so you have an idea of what I want to achieve. It's quiet lengthy but I tried.
import React, { useEffect, useState } from "react";
/*
loads of imports
**/
const ReplyFeedback = (props) => {
const [dataset, setdataset] = useState([]);
const [replyset, setreplyset] = useState([]);
const [visible, setvisible] = useState(null);
const [messagereply, setmessagereply] = useState("");
const [msgid, setmsgid] = useState(null);
const dispatch = useDispatch();
/**
* useEffects and functions
*/
const handleSaveReply = (id) => {
/**
* Saving the reply from text input for firebase
*/
};
const handleCaseupdate = async () => {
/**
* Some firebase events
*/
};
function processreply(id) {
setmsgid(id);
setvisible(true);
}
const renderfeedback = (id, message, date, index) => (
/**
* reder some jsx
*/
);
const renderreply = (item, index) => (
/**
* render some jsx
*/
);
const ModalPopup = ({ visible, children }) => {
const [showmodal, setshowmodal] = useState(visible);
const togglemodal = () => {
if (visible) {
setshowmodal(true);
} else {
setshowmodal(false);
}
};
return (
<Modal transparent visible={showmodal}>
<View
style={{
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center",
alignItems: "center",
}}
>
<View style={styles.modalContainer}>{children}</View>
</View>
</Modal>
);
};
return (
<React.Fragment>
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior="position">
<ModalPopup visible={visible}>
<View
style={{
width: "90%",
height: "72%",
backgroundColor: "rgb(255,255,255)",
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "center",
}}
>
<TextInput
placeholder="Enter Reply"
multiline
autoCorrect
scrollEnabled
numberOfLines={8}
autoCapitalize="sentences"
placeholderTextColor="rgb(0,191,255)"
onChangeText={(text) => setmessagereply(text)}
value={messagereply}
blurOnSubmit={false}
style={{
width: "100%",
height: 250,
fontSize: 20,
padding: 10,
textAlignVertical: "top",
backgroundColor: "rgb(245,245,245)",
}}
/>
</View>
<View
style={{
marginTop: 15,
flexDirection: "row",
justifyContent: "space-around",
}}
>
<TouchableOpacity
style={{
width: 65,
height: 65,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(109, 123, 175)",
}}
onPress={() => setvisible(false)}
>
<MaterialIcons
name="cancel"
size={50}
color="rgb(225,225,225)"
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => handleSaveReply(msgid)}
style={{
width: 65,
height: 65,
borderRadius: 30,
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(109, 123, 175)",
}}
>
<Ionicons name="send" size={40} color="rgb(255,255,255)" />
</TouchableOpacity>
</View>
</View>
</ModalPopup>
</KeyboardAvoidingView>
<View style={styles.feedbackreply}>
<View style={styles.replydetails}>
{/**
* some jsx
*/}
</View>
{/**
* somejsx
*/}
</View>
<View
style={{
alignSelf: "flex-end",
marginHorizontal: 5,
marginTop: 5,
}}
>
{/**
* some jsx
*/}
</View>
<ScrollView
automaticallyAdjustContentInsets={false}
keyboardShouldPersistTaps="handled"
>
{dataset &&
dataset
.sort(
(a, b) =>
new moment(new Date(a.date)).format("YYYYMMDD HHmmss") -
new moment(new Date(b.date)).format("YYYYMMDD HHmmss")
)
.map(({ id, message, date }, index) =>
renderfeedback(id, message, date, index)
)}
<ScrollView keyboardShouldPersistTaps="handled">
{replyset &&
replyset
.sort(
(a, b) =>
new moment(new Date(a.date)).format("YYYYMMDD HHmmss") -
new moment(new Date(b.date)).format("YYYYMMDD HHmmss")
)
.filter((item) => item.id !== id)
.map((item, index) => renderreply(item, index))}
</ScrollView>
</ScrollView>
</SafeAreaView>
</React.Fragment>
);
};
export { ReplyFeedback };
const window = Dimensions.get("window");
const styles = StyleSheet.create({
/**
* Tones of styling
*/
});
So you could see from the container I rendered some other views including a scrollview that all appears under the modal when it is shown. Please the issue here is that the modal flickers along with the keyboard making it impossible to add text
This is the image

Related

How can I store the value of TextInput to local storage and get them when the app starts in react native?

I'm making a to-do list app. I need to store the input value locally or somewhere so that I can show them in the app even if the app is opened after closing once. Now when I'm closing the app and restarting all the previous values is being vanished. But I want to keep the previous data and If new data is given that will also be stored if I don't delete that manually. How can I solve this problem? I'm using expo.
Here is my code:
import React, { useState } from 'react';
import { View, Text, StyleSheet, Button, FlatList, TouchableOpacity, TextInput, ScrollView } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons'
import Line from './Line';
function App() {
//storing data in array
const [initialElements, changeEl] = useState([
]);
const [value, setValue] = useState('')
const [idx, incr] = useState(1);
const [exampleState, setExampleState] = useState(initialElements);
//add button
const addElement = () => {
if (value != '') {
var newArray = [...initialElements, { id: idx, title: value }];
incr(idx + 1);
setExampleState(newArray);
changeEl(newArray);
}
}
//delete button
const delElement = (id) => {
let newArray = initialElements.filter(function (item) {
return item.id !== id
})
setExampleState(newArray);
changeEl(newArray);
}
//showing item
const Item = ({ title, id }) => (
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
<View style={{ borderWidth: 2, margin: 5, flex: 1, padding: 10, borderRadius: 10 }}>
<TouchableOpacity onPress={() => { alert(title) }}>
<Text >{title}</Text>
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => delElement(id)}>
<MaterialIcons name='delete' size={24} />
</TouchableOpacity>
</View>
);
//calling item for render
const renderItem = ({ item }) => {
<Item title={item.title} id={item.id} />
}
return (
<View style={styles.container}>
<View style={{ alignItems: 'center', marginBottom: 10 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}> To <Text style={{ color: 'skyblue', fontWeight: '900' }}>Do</Text> List</Text>
<Line />
</View>
<View>
<TextInput onChangeText={text => setValue(text)} placeholder="Enter to Todo" style={{ borderWidth: 2, borderRadius: 10, backgroundColor: 'skyblue', height: 40, paddingLeft: 10, borderColor: 'black' }}></TextInput>
</View>
<View style={{ alignItems: 'center' }}>
<TouchableOpacity onPress={() => addElement()} style={{ marginTop: 10 }}>
<View style={{ backgroundColor: 'black', width: 70, height: 40, justifyContent: 'center', borderRadius: 10 }}>
<Text style={{ color: 'white', textAlign: 'center' }}>Add</Text>
</View>
</TouchableOpacity>
</View>
<FlatList
style={{ borderWidth: 2, padding: 10, flex: 1, backgroundColor: '#EEDDD0', marginTop: 10 }}
data={exampleState}
renderItem={renderItem}
keyExtractor={item => item.id.toString()
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
margin: 5,
}
})
export default App;
React Native provides a local storage option called AsyncStorage. You might use asyncstorage to save the data locally on the device and get that data inside useEffect() hook on startup.
You can find more about AsyncStorage here.
Although this is deprecated and now community package " #react-native-async-storage/async-storage " is used. The implementation remains the same.

How to save route.params with asyncstorage?

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

How To Implement Auto Scrolling Video in React Native using JSX

import React, {useState, useEffect, useRef} from 'react';
import {
View,
Text,
StyleSheet,
FlatList,
ScrollView,
Image,
TouchableHighlight,
TextInput,
TouchableOpacity,
Modal,
Dimensions,
SafeAreaView,
} from 'react-native';
import Voice from 'react-native-voice';
import Video from 'react-native-video';
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import {videos} from './data';
// code started creating state on every event which will be used
enter center export default function App() {
const [pitch, setPitch] = useState('');
const [error, setError] = useState('');
const [end, setEnd] = useState('');
const [started, setStarted] = useState('');
const [results, setResults] = useState([]);
const [videoData, setVideos] = useState([...videos]);
const [showModal, setShowModal] = useState(false);
const [paused, setPaused] = useState(true);
const [position, setPosition] = useState({start: null, end: null});
const [muted, setMuted] = useState(true);
//Setting callbacks for the process status
useEffect(() => {
Voice.onSpeechStart = onSpeechStart;
Voice.onSpeechEnd = onSpeechEnd;
Voice.onSpeechError = onSpeechError;
Voice.onSpeechResults = onSpeechResults;
return () => {
//destroy the process after switching the screen
Voice.destroy().then(Voice.removeAllListeners);
};
}, []);
const onSpeechStart = (e) => {
//Invoked when .start() is called without error
console.log('onSpeechStart: ', e);
setStarted('√');
};
const onSpeechEnd = (e) => {
//Invoked when SpeechRecognizer stops recognition
console.log('onSpeechEnd: ', e);
setEnd('√');
};
const onSpeechError = (e) => {
//Invoked when an error occurs.
console.log('onSpeechError: ', e);
setError(JSON.stringify(e.error));
};
2. in this part we are matching the word sopken by the user and checking is if the same video is available in our data if it si then the video will come on the top
const onSpeechResults = (e) => {
//Invoked when SpeechRecognizer is finished recognizing
console.log('onSpeechResults: ', e.value[0]);
let vResult = e.value[0];
let small = vResult.toLowerCase();
setResults([small]);
setShowModal(false);
for (let i = 0; i < videoData.length - 1; i++) {
let newArray = [...videos];
if (small == newArray[i].name) {
newArray.splice(0, 0, newArray[i]);
const mainArray = newArray.filter((item, index) => {
if (item.name == small && index != 0) {
} else {
return item;
}
});
setVideos(mainArray);
}
}
};
const startRecognizing = async () => {
console.log('start listenind');
//Starts listening for speech for a specific locale
try {
await Voice.start('en-US');
setPitch('');
setError('');
setStarted('');
setResults([]);
setEnd('');
} catch (e) {
//eslint-disable-next-line
console.error(e);
}
};
const stopRecognizing = async () => {
//Stops listening for speech
try {
await Voice.stop();
} catch (e) {
//eslint-disable-next-line
console.error(e);
}
};
3. making video play n pause is out of axis
onScroll = (event) => {
const scrollPosition = event.nativeEvent.contentOffset.y;
const paused = paused;
const {start, end} = position;
if (scrollPosition < start && scrollPosition > end && paused) {
setPaused(false);
} else if (scrollPosition > start && scrollPosition < end && !paused) {
setPaused(true);
} else if (scrollPosition < end && !paused) {
setPaused(true);
}
};
4. getting the scroll y axis
const threshold = 180;
onVideoLayOut = (event) => {
const {height} = Dimensions.get('window');
const start = -(event.nativeEvent.layout.y - height + threshold);
const end =
event.nativeEvent.layout.y + event.nativeEvent.layout.height - threshold;
// console.log(start, end, 'position');
setPosition((state) => {
return {
...state,
start: start,
end: end,
};
});
console.log(position, 'position1');
};
5.this the render par using faltlist to render data
return (
<View
style={{
flex: 1,
backgroundColor: 'black',
}}>
<View
style={{
height: 70,
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 15,
flexDirection: 'row',
marginVertical: 15,
marginTop: 25,
}}>
<View
style={{
height: 40,
borderRadius: 20,
width: 300,
borderWidth: 1,
borderColor: '#F23C29',
}}>
<TextInput
placeholder="Search ..."
style={{flex: 1, paddingHorizontal: 30, color: 'white'}}
placeholderTextColor="grey"
/>
</View>
<View>
<TouchableOpacity
onPress={() => {
startRecognizing();
setShowModal(true);
}}>
<SimpleLineIcons color={'white'} name="microphone" size={30} />
</TouchableOpacity>
</View>
</View>
<SafeAreaView
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
borderColor: 'orange',
borderWidth: 1,
}}>
<FlatList
data={videoData}
// scrollEventThrottle={16}
onScroll={onScroll}
showsVerticalScrollIndicator={true}
renderItem={({item, index}) => {
return (
<View
key={index}
style={{
height: 250,
marginVertical: 20,
}}>
<Video
source={{uri: item.source}}
onLayout={onVideoLayOut}
muted={true}
resizeMode="cover"
paused={!paused}
style={{
width: '100%',
height: undefined,
aspectRatio: 2,
marginBottom: 200,
marginTop: 300,
}}
/>
<Text
style={{
fontSize: 20,
alignItems: 'center',
marginLeft: 20,
marginTop: 15,
color: 'white',
}}>
{item.title}
</Text>
</View>
);
}}
/>
</SafeAreaView>
{/* <ScrollView style={{flex: 2, width: '100%'}}>
{videoData.map((item, index) => {
return (
<View
key={index}
style={{
height: 250,
marginVertical: 20,
}}>
<Video
source={{uri: item.source}}
paused={false}
rate={1.0}
volume={1.0}
isMuted={false}
resizeMode="contain"
shouldPlay
isLooping
style={{width: '100%', height: undefined, aspectRatio: 2}}
onError={(e) => {
console.log(e, 'video data');
}}
/>
<Text
style={{
fontSize: 20,
alignItems: 'center',
marginLeft: 20,
marginTop: 15,
color: 'white',
}}>
{item.title}
</Text>
</View>
);
})}
</ScrollView> */}
<Modal
visible={showModal}
transparent={true}
onRequestClose={() => {
setShowModal(false);
}}>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.9)',
}}>
<MaterialCommunityIcons
name="microphone-settings"
size={45}
color="white"
/>
<Text
style={{
color: '#fff',
textAlign: 'center',
marginTop: 20,
fontSize: 17,
}}>
We are listening
</Text>
</View>
</Modal>
{/* <View style={{justifyContent: 'center', alignItems: 'center'}}>
<Text>Video</Text>
<View>
<TouchableHighlight
onPress={stopRecognizing}
style={styles.buttonStyle}>
<Text style={styles.buttonTextStyle}>Stop</Text>
</TouchableHighlight>
</View>
<View>
<TouchableHighlight onPress={startRecognizing}>
<SimpleLineIcons name="microphone" size={25} />
</TouchableHighlight>
</View>
{results.map((item, index) => {
return (
<Text key={`result-${index}`} style={styles.textStyle}>
{item}
</Text>
);`enter code here`
})}
<Text>Results</Text>
</View> */}
</View>
);
}
const styles = StyleSheet.create({
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
});
The question is in my flat list at a time, there are three videos which are render .. I was trying to make the first video play and rest to be in paused state and after a 5 second of interval, the next video should be played
I tried demo which looks like instagram where the video which is in focused is played automatically and rest all are paused
I tried using scroll props and also video-autoscroll lib but coudlnot resolve
the videos are getting render but

expo camera show black screen for first time : React-Native

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

How change the styling of dynamically generated JSX via a button click in React Native

I am generating JSX code based on an array values that I get from a backend API. As shown in the image below I am generating those boxes based on the length of the array. What I want is when I click on any of those boxes the background colour changes.
I want these boxes to behave like a radio button, so only one box has a different background colour at a time. The array name is "hasMultipleWeights".
I only included the relevant parts of the code ...
const ProductDetailsScreen = (props) => {
const productId = props.navigation.getParam("productId");
const selectedProduct = useSelector((state) =>
state.products.products.find((prod) => prod.id === productId)
);
const productsMultipleWeights = useSelector(
(state) => state.products.productsMultipleWeights
);
var hasMultipleWeights = productsMultipleWeights.find(
(prod) => Object.keys(prod)[0] == selectedProduct.id
);
if (hasMultipleWeights) {
hasMultipleWeights = hasMultipleWeights[Object.keys(hasMultipleWeights)[0]];
}
return (
<ScrollView style={{}}>
<View style={styles.screen}>
{hasMultipleWeights && (
<View style={{ alignItems: "center" }}>
<ScrollView
horizontal
contentContainerStyle={{ padding: 2 }}
showsHorizontalScrollIndicator={false}
>
{hasMultipleWeights.map((item) => (
<TouchableOpacity
key={item.id}
onPress={() => {}}
style={{
...styles.productOptions,
backgroundColor: 'white',
}}
>
<Text style={styles.productWeightVolumUnit}>
<Text style={styles.productWeightVolumeValue}>
{NS(item.weight, "Arabic")}
</Text>
{" "}
{selectedProduct.weightVolumeUnit}
</Text>
<MaterialCommunityIcons
name={
selectedProduct.weightVolumeUnit === "كغ"
? "weight-kilogram"
: selectedProduct.weightVolumeUnit === "مل"
? "water"
: "weight-gram"
}
size={26}
color="grey"
style={styles.weightIcon}
/>
</TouchableOpacity>
))}
</ScrollView>
</View>
)}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
productOptions: {
shadowColor: "black",
shadowOpacity: 0.26,
elevation: 5,
borderRadius: 10,
backgroundColor: "white",
width: 85,
height: 65,
marginHorizontal: 6,
alignItems: "center",
justifyContent: "center",
shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
},
});
For changing the color dynamically you have to use state. So, create a new state to check the button that is "checked", you change it in your onPress method and then make the validation.
const ProductDetailsScreen = (props) => {
const [checkedButton, setCheckedButton] = React.useState('')
const productId = props.navigation.getParam("productId");
const selectedProduct = useSelector((state) =>
state.products.products.find((prod) => prod.id === productId)
);
const productsMultipleWeights = useSelector(
(state) => state.products.productsMultipleWeights
);
var hasMultipleWeights = productsMultipleWeights.find(
(prod) => Object.keys(prod)[0] == selectedProduct.id
);
if (hasMultipleWeights) {
hasMultipleWeights = hasMultipleWeights[Object.keys(hasMultipleWeights)[0]];
}
return (
<ScrollView style={{}}>
<View style={styles.screen}>
{hasMultipleWeights && (
<View style={{ alignItems: "center" }}>
<ScrollView
horizontal
contentContainerStyle={{ padding: 2 }}
showsHorizontalScrollIndicator={false}
>
{hasMultipleWeights.map((item) => (
<TouchableOpacity
key={item.id}
onPress={() => setCheckedButton(item.id)}
style={{
...styles.productOptions,
backgroundColor: checkedButton === item.id ? "grey" : "white",
}}
>
<Text style={styles.productWeightVolumUnit}>
<Text style={styles.productWeightVolumeValue}>
{NS(item.weight, "Arabic")}
</Text>
{" "}
{selectedProduct.weightVolumeUnit}
</Text>
<MaterialCommunityIcons
name={
selectedProduct.weightVolumeUnit === "كغ"
? "weight-kilogram"
: selectedProduct.weightVolumeUnit === "مل"
? "water"
: "weight-gram"
}
size={26}
color="grey"
style={styles.weightIcon}
/>
</TouchableOpacity>
))}
</ScrollView>
</View>
)}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
productOptions: {
shadowColor: "black",
shadowOpacity: 0.26,
elevation: 5,
borderRadius: 10,
backgroundColor: "white",
width: 85,
height: 65,
marginHorizontal: 6,
alignItems: "center",
justifyContent: "center",
shadowRadius: 2,
shadowOffset: { width: 0, height: 1 },
},
});
Seems like you need to create checkedId state
const [ checkedId, setCheckedId ] = useState('')
useEffect(() =>
// set first box to red at first render
hasMultipleWeights && setCheckedId(hasMultipleWeights[0].id) ,[ hasMultipleWeights ])
...
<TouchableOpacity
key={item.id}
onPress={() =>setCheckedId(item.id)}
style={{
...styles.productOptions,
backgroundColor: item.id == checkedId ? 'red' : 'white',
}}
>

Categories

Resources