On my screen, I type in the input field and get search results accordingly. The list is rendered within a ScrollView but it still doesn't let me scroll when the keypad is open (in Android at least).
How can I fix this?
This is the component where the scroll view is rendered.
export const LocationsFound: React.FunctionComponent<LocationsFoundProps> = ({
addressesFound,
}) => {
return (
<>
{addressesFound.length > 0 ? (
<KeyboardAwareScrollView
style={styles.searchResultsContainer}
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
>
{addressesFound.map((addressDetails: addressDetailsType) => {
return (
<View
key={addressDetails.placeName}
style={styles.resultContainer}>
<Text
style={styles.text}
onPress={() => handleLocationSelection(addressDetails)}>
{addressDetails.placeName}
</Text>
</View>
);
})}
</KeyboardAwareScrollView>
) : null}
</>
);
};
const styles = StyleSheet.create({
searchResultsContainer: {
width: moderateScale(400),
paddingHorizontal: moderateScale(50),
paddingRight: moderateScale(65),
marginTop: moderateScale(10),
},
resultContainer: {
marginTop: moderateScale(10),
borderBottomWidth: 1,
borderBottomColor: 'grey',
},
text: {
fontSize: moderateScale(15),
},
});
This is the component where the LocationsFound component is called.
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<View style={styles.container}>
<View style={styles.searchFieldContainer}>
<AddressSearchInput
addressType="favouritePoint"
placeholder="Ort eingeben"
/>
</View>
<View style={styles.dropdown}>
<LocationsFound
addressesFound={locations.addressesFoundList}
/>
</View>
</View>
</SafeAreaView>
);
};
export const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
container: {
height: '100%',
backgroundColor: 'white',
width: '100%',
display:"flex",
flexDirection:"column",
flex: 1
},
dropdown: {
position: 'absolute',
top: moderateScale(215),
zIndex: moderateScale(10),
backgroundColor: '#fff',
flex: 1
},
});
I also tried adding
onScrollBeginDrag={Keyboard.dismiss}
but it doesn't make a difference.
sounds like a height, issue, without all the code, no one is going to give your clear answers without speculation. the keyboard doesn't shrink the view, check out this package, it may help - https://github.com/APSL/react-native-keyboard-aware-scroll-view
Related
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.
I am using a thumbnail where the cross is transparent and takes the background color of the main container. I want the cross to be white.
export const FriendDetailsScreen: React.FunctionComponent = () => {
const navigation = useNavigation();
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<View style={styles.container}>
<View style={styles.iconsContainer}>
<TouchableOpacity
style={styles.cross}>
<Thumbnail
source={{
uri:
'https://cdn0.iconfinder.com/data/icons/very-basic-android-l-lollipop-icon-pack/24/close-512.png',
}}
/>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
);
};
export const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
container: {
backgroundColor: '#323443',
flex: 1,
},
cross: {
paddingTop: moderateScale(30),
paddingLeft: moderateScale(20),
zIndex: 100,
},
});
If I add a background color to cross or the TouchableOpacity a block of white appears which goes beyond the thumbnail. How else can I achieve this?
Thumbnail:
https://cdn0.iconfinder.com/data/icons/very-basic-android-l-lollipop-icon-pack/24/close-512.png
Have you tried passing in a style prop to thumbnail like this?
export const FriendDetailsScreen: React.FunctionComponent = () => {
const navigation = useNavigation();
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<View style={styles.container}>
<View style={styles.iconsContainer}>
<TouchableOpacity
style={styles.cross}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn0.iconfinder.com/data/icons/very-basic-android-l-lollipop-icon-pack/24/close-512.png',
}}
/>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
);
};
export const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
container: {
backgroundColor: '#323443',
flex: 1,
},
cross: {
paddingTop: moderateScale(30),
paddingLeft: moderateScale(20),
zIndex: 100,
},
thumbnail: {
backgroundColor: '#323443'
}
});
I'm adding a search bar with button (i.e. search icon) in navigationOptions of React navigation. Now I want to send the typed text with navigate function. I don't know how to pass reference of TextInput to get its native text. Following is the code I've done so far.
static navigationOptions = ({ navigation }) => (
headerStyle: {
...
},
headerTitle: (
<View style={{ width: '100%', height: 75, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRadius: 50, backgroundColor: 'white' }}>
<TextInput style={{ width: '85%', padding: 15 }}
onSubmitEditing={
(e) => (e.nativeEvent.text.length > 2) && navigation.navigate('Brands', {text: e.nativeEvent.text})
} />
<TouchableOpacity style={{ width: '15%', padding: 15 }}
onPress={() => navigation.navigate('Brands'/*, {text: HERE I WANT TO GET TEXT FROM REFERENCE}*/)}>
<Icon type='FontAwesome' name='search'
style={{ color: 'red', 20, textAlign: 'center' }} />
</TouchableOpacity>
</View>
)
)};
But as navigationOptions is static, I can't use this keyword inside it. Also I can't use state inside navigationOptions as well. So how could I use ref to get input text on button press.
EDIT
I want to get TextInput value from ref
<TextInput ref={(ref) => { this.input = ref; }} />
// OR
<TextInput ref='input' />
So that I can get its value from
this.input._lastNativeText
// OR
this.refs['input']._lastNativeText
But I cant use this keyword or state in navigationOptions because its static. How can I access the value of TextInput on button click?
send callback to navigationOptions through naviagtion prop .
set callback value in componentDidMount
componentDidMount=()=> {
this.props.navigation.setParams({
onSubmitEditing: this.onSubmitEditing,
});
}
onSubmitEditing=(event)=>{
console.log(event);
//this is avaialbel here
}
Get on SubmitEdit function from naviagttion params
static navigationOptions = ({ navigation = this.props.navigation }) => {
const onSubmitEditing = navigation.state.params.onSubmitEditing;
return {
headerTitle: (
<View style={{ width: '100%', height: 75, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRadius: 50, backgroundColor: 'white' }}>
<TextInput style={{ width: '85%', padding: 15 }}
onSubmitEditing={onSubmitEditing} />
<TouchableOpacity style={{ width: '15%', padding: 15 }}
onPress={() => navigation.navigate('Brands'/*, {text: HERE I WANT TO GET TEXT FROM REFERENCE}*/)}>
<Icon type='FontAwesome' name='search'
style={{ color: 'red', size: 20, textAlign: 'center' }} />
</TouchableOpacity>
</View>
)
}
};
Just in case anybody needs this
componentDidMount(){
---
this.props.navigation.setParams({
setMenuRef: this.setMenuRef.bind(this),
});
---
}
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
onSortClick() {
this._menu.show();
}
static navigationOptions = ({navigation}){
headerRight:(
---
ref={navigation.state.params && navigation.state.params.setMenuRef}
---
)
}
Lets say i have <ListingCard/> components inside of a
<ScrollView/> .
What i want is render those <ListingCard/> side by side on a container class.
Here is What i have tried so far:
<ListingCard/>
const ListingCard = (props) => {
return (
<View style={styles.container}>
<Text>This is ListingCard Component</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
//flex: 1,
alignItems: 'center',
justifyContent: 'center',
height: 150,
width: Dimensions.get('window').width / 2 - 6,
backgroundColor: colors.WHITE,
borderRadius: 5,
marginHorizontal:10,
marginBottom: 10
},
});
export default ListingCard;
here is how i used <ListingCard/> :
render() {
const { currentCategory } = this.state;
return (
<Drawer
ref={(ref) => this._drawer = ref}
type="static"
onOpenStart={() => {
this.setState({
isDrawerOpen: true,
})
}}
onClose={() => {
this.setState({
isDrawerOpen: false,
})
}}
content={<SideFilterMenu />}
tapToClose={true}
side={'right'}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
>
<View style={styles.container}>
<CustomHeader
onPress={() => this.handleFilterPress()}
headerText={currentCategory && currentCategory.categoryName}
isIconVisible={true}
rightButtonText={'Filtrele'}
onIconPress={() => this.handleBackPress()}
/>
<ScrollView
style={{flex:1}}
contentContainerStyle={styles.cardContainer}
>
<ListingCard />
<ListingCard />
<ListingCard />
<ListingCard />
<ListingCard />
<ListingCard />
<ListingCard />
<ListingCard />
</ScrollView>
</View>
</Drawer>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
//alignItems: 'center',
//justifyContent: 'center',
backgroundColor: colors.GENERAL_BCK,
//paddingHorizontal: 5
},
cardContainer: {
flexDirection: 'row',
flexWrap: 'wrap'
}
});
What i have tried is, with or without <View> container but this isnt helped at all. The reason i could not accoplish it is i am relatively new on ReactNative and have struggle with those stylings.
I could not accomplish to render those <ListingCard/> side by side. Any help will be appreciated, thanks.
Ok, i have fixed it.
Problem was <ListingCard/>'s width.
it was: width: Dimensions.get('window').width / 2 - 6, which was too wide to fit two of them on same row. so i changed it to: width: Dimensions.get('window').width / 2 - 20, and voila! it's worked.
I have a View with few form elements and a button (TouchableHighlight). On clicking the button, an Activity Indicator should be shown as an overlay to the existing view. The Activity Indicator should be centered within the page and the existing view should be slightly blurred to indicate overlay. I tried different options but could not get it to work.
render() {
const { username, password } = this.state;
return (
<View style={styles.container}>
<View style={styles.group}>
<Text style={styles.text}>Username:</Text>
<TextInput
style={styles.input}
onChangeText={this.handleUserNameChange.bind(this)}
value={username}
underlineColorAndroid="transparent"
/>
</View>
<View style={styles.group}>
<Text style={styles.text}>Password:</Text>
<TextInput
style={styles.input}
secureTextEntry={true}
onChangeText={this.handlePasswordChange.bind(this)}
value={password}
underlineColorAndroid="transparent"
/>
</View>
<TouchableHighlight
style={styles.button}
onPress={this.handleLogin.bind(this)}>
<Text style={styles.buttonText}>Logon</Text>
</TouchableHighlight>
</View>
);
}
Existing styles:
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: '#F5FCFF',
marginTop: 60
},
group: {
alignItems: 'flex-start',
padding: 10
},
input: {
width: 150,
padding: 2,
paddingLeft: 5,
borderColor: 'gray',
borderWidth: 1
},
text: {
padding: 0
},
button: {
width: 150,
backgroundColor: '#2299F2',
padding: 15,
marginTop: 20,
borderRadius: 5
},
buttonText: {
textAlign: 'center',
color: '#fff',
fontSize: 24
},
});
I need to an ActivityIndicator to the above View, overlay the view, and center the ActivityIndicator.
For this to work, you'd need to absolute position it, and render it after the elements that should be underneath the overlay:
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
}
Then simply compose it into the render method conditionally, based on a loading state. I am going to assume this.handleLogin sets some sort of loading state already.
Make sure it's rendered last so it takes precedence.
...
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator size='large' />
</View>
}
Here is a complete example using create react native app.
import React from 'react';
import {StyleSheet, ActivityIndicator, View} from "react-native";
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {}
render() {
return (
<View
style={{flex: 1}}
>
//Add other content here
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator/>
</View>
}
</View>
);
}
}
showLoading() {
this.setState({loading: true})
}
hideLoading() {
this.setState({loading: false})
}
const styles = StyleSheet.create({
loading: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
opacity: 0.5,
backgroundColor: 'black',
justifyContent: 'center',
alignItems: 'center'
}
})
You can use StyleSheet.absoluteFill to shorten code.
Add this to your render:
<View style={styles.container}>
//... other code here
{this.state.loading && <View
style={{
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
}}>
<ActivityIndicator />
</View>}
</View>
Improvement:
You can also create a Loading component:
Loading.js
import React from 'react';
import {View, ActivityIndicator, StyleSheet} from 'react-native';
export const Loading = ({theme = 'white', size = 'large'}) => {
const color = theme === 'white' ? '#00bdcd' : '#fff';
return (
<View
style={{
...StyleSheet.absoluteFill,
justifyContent: 'center',
alignItems: 'center',
}}>
<ActivityIndicator size={size} color={color} />
</View>
);
};
Then use it anywhere you want
<View style={styles.container}>
//... other code here
// remember to import Loading component
{this.state.loading && <Loading />}
</View>
You can build a nice overlay using the activity indicator component by also leveraging the modal capabilities like Sanaur suggests.
For example you can use the below functional component. You can control it's visibility through the show prop that you can tie to a state in your screen.
An example that you can adapt to your needs.
const ModalActivityIndicator = props => {
const {
show = false,
color = "black",
backgroundColor = "white",
dimLights = 0.6,
loadingMessage = "Doing stuff ..."
} = props;
return (
<Modal transparent={true} animationType="none" visible={show}>
<View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: `rgba(0,0,0,${dimLights})`
}}
>
<View
style={{
padding: 13,
backgroundColor: `${backgroundColor}`,
borderRadius: 13
}}
>
<ActivityIndicator animating={show} color={color} size="large" />
<Text style={{ color: `${color}` }}>{loadingMessage}</Text>
</View>
</View>
</Modal>
);
};
and in your screen, in the render's return, just add it there as a child (please ignore the rest of the code, I put it there for context).
return (
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss();
}}
>
<View style={{ padding: 13, flex: 1}}>
<ModalActivityIndicator show={screenIsWaiting} />
<View
style={{
where screenIsWaiting is just a state, for example
const [screenIsWaiting, setScreenIsWaiting] = useState(false);
To test it you can add a button somewhere,
<Button
title="TestButton"
onPress={async () => {
setScreenIsWaiting(true);
await sleep(5000);
setScreenIsWaiting(false);
...
}}
/>
where sleep is a function defined as
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
I found the sleep() idea on stackoverflow on another post.
You can of course also define the
<ModalActivityIndicator show={screenIsWaiting} ... />
only once in your App's root component and trigger it's display and props via a global state container like redux.
There is a library available for this react-native-loading-spinner-overlay.
You can simply install it using
npm install react-native-loading-spinner-overlay --save
and can import into your project using
import Spinner from 'react-native-loading-spinner-overlay';
Here is how to use it
<Spinner
//visibility of Overlay Loading Spinner
visible={this.state.loading}
//Text with the Spinner
textContent={'Loading...'}
//Text style of the Spinner Text
textStyle={styles.spinnerTextStyle}
/>
STEP 1:
Create the component for the spinner:
export const OverlaySpinner = () => {
return (
<View style={styles.spinnerView}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
};
STEP 2:
Create the style for the spinner view (using zIndex is very important to make sure the view is over everything on the screen):
spinnerView: {
position: "absolute",
zIndex: 1,
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#F5FCFF88",
},
STEP 3:
Make the initial state for the spinning component:
const [showSpinner, setshowSpinner] = useState(true);
STEP 4:
Use the component and don't forget to import it (don't forget to dismiss the keyboard on submit)
{showSpinner && <OverlaySpinner />}
I suppose you should use Modal to overlay activity indicator. Following is an example:
<Modal
transparent={true}
animationType={'none'}
visible={loading}
onRequestClose={() => {console.log('close modal')}}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator
animating={loading} />
</View>
</View>
</Modal>
Add in view of loading
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
set in View of Activity Indicator
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
Here my code for a functional component for anyone looking to achieve this with nativebase as design system. useColorScheme is another hook I use for detecting dark mode.
import { Flex, Spinner } from "native-base"
import React from "react"
import useColorScheme from "../../hooks/useColorScheme"
export default function Loading() {
const colorScheme = useColorScheme()
return (
<Flex
position="absolute"
alignItems="center"
justifyContent="center"
top={0}
left={0}
right={0}
bottom={0}
backgroundColor={colorScheme === "dark" ? "coolBlack.500" : "white"}
>
<Spinner size="lg" color={colorScheme === "dark" ? "white" : "coolBlack.500"} />
</Flex>
)
}