In an App created with npx react-native init MyApp
I am having a problem that I cannot correct.
I have a section to upload images and text to a database, Firebase.
On the screen I have a button to send the Post, as I show in the first image.
When I start to type the text and it is abundant, the Send button disappears, and I cannot send the text since I cannot leave the keyboard.
The problem also arises when I add an image, since it occupies more and the send button also disappears, and I cannot leave the keyboard.
I have added ScroolView to the View that contains this screen, but it breaks the application, brings the items to the top of the screen.
On other screens, I remove the lower task bar with a function to have more space for the device's keyboard, but this time I don't know how to solve the problem.
In iOS it is totally impossible to leave the keyboard to access the Send Post Button.
On Android devices, the Button is slightly displayed, as I show in the Android image, and also the screen automatically Scrools as I add text.
However, on iOS this does not happen, and I cannot send the Post.
How can I correct this error?
How can I make the application keyboard disappear when I want it to?
I EDIT THE QUESTION WITH MORE INFORMATION
I have tried a solution that the user #MichaelBahl has offered me,
adding KeyboardAvoidingView, and with the text it works, the text scrolls towards the top and always but if we add a large amount of Text and an image, the Send Button disappears at the bottom both in iOS and Android, making it impossible to send of the message.
How to fix this problem on iOS and Android?
I will continue looking for solutions.
I have added a styles file that I forget, these styles with "styled-components" are what create the screen
Adding seems to work, but when I open that screen the buttons are very close to the top, and I can't display the "add image" buttons
I edit the question again
I have used <KeyboardAwareScrollView> as suggested by user #MuhammadNuman.
This seems to work, but when starting on this screen, the buttons are at the top and thus prevent you from using the buttons on the right side to add images.
All this can happen due to the styles used in my TextInput, but I don't know how to correct it.
I show a new screenshot and a video with the operation of the buttons
import React, { useContext, useState } from "react"
import {
View,
Text,
Button,
Alert,
ActivityIndicator,
ScrollView,
TouchableWithoutFeedback,
Keyboard,
KeyboardAvoidingView,
} from "react-native"
import ActionButton from "react-native-action-button"
import Icon from 'react-native-vector-icons/Ionicons'
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'
import ImagePicker from "react-native-image-crop-picker"
import storage from '#react-native-firebase/storage'
import firestore from '#react-native-firebase/firestore'
import globalStyles from "../styles/global"
import {
InputField,
InputWrapper,
AddImage,
SubmitBtn,
SubmitBtnText,
StatusWrapper
} from '../styles/AddPostStyles'
import { AuthContext } from "../navigation/AuthProvider"
const AddPostScreen = () => {
const { user, logout } = useContext(AuthContext)
const [image, setImage] = useState(null)
const [uploading, setUploading] = useState(false)
const [transferred, setTransferred] = useState(0)
const [post, setPost] = useState(null)
const takePhotoFromCamera = () => {
ImagePicker.openCamera({
width: 1200,
height: 780,
cropping: true,
}).then((image) => {
console.log(image)
const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path
setImage(imageUri)
})
}
const choosePhotoFromLibrary = () => {
ImagePicker.openPicker({
width: 1200,
height: 780,
cropping: true,
}).then((image) => {
console.log(image)
const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path
setImage(imageUri)
})
}
const submitPost = async () => {
const imageUrl = await uploadImage()
console.log('Image Url', imageUrl)
firestore()
.collection('posts')
.add({
userId: user.uid,
post: post,
postImg: imageUrl,
postTime: firestore.Timestamp.fromDate(new Date()),
likes: null,
comments: null
})
.then(() => {
console.log('Post Added...')
Alert.alert(
'Post published!',
'Your post has been published Successfully!',
)
setPost(null)
})
.catch((error) => {
console.log('Something went wrong with added post to firestore.', error)
})
}
const uploadImage = async () => {
if (image == null) {
return null
}
const uploadUri = image
let filename = uploadUri.substring(uploadUri.lastIndexOf('/') + 1)
// Add timestad to File Name
const extension = filename.split('.').pop()
const name = filename.split('.').slice(0, -1).join('.')
filename = name + Date.now() + '.' + extension
setUploading(true)
setTransferred(0)
const storageRef = storage().ref(`photos/${filename}`)
const task = storageRef.putFile(uploadUri)
// Set transferred state
task.on('state_changed', (taskSnapshot) => {
console.log(`${taskSnapshot.bytesTransferred} transferred out of ${taskSnapshot.totalBytes}`)
setTransferred(
Math.round(taskSnapshot.bytesTransferred / taskSnapshot.totalBytes) * 100
)
})
try {
await task
const url = await storageRef.getDownloadURL()
setUploading(false)
setImage(null)
/* Alert.alert(
'Imagen subida!',
'Tu imagen se subio correctamente!',
) */
return url
} catch (e) {
console.log(e)
return null
}
}
return (
<KeyboardAvoidingView
behavior={Platform.OS === "android" ? "padding" : "height"}
style={{ flex: 1 }}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={globalStyles.container}>
<InputWrapper>
{image != null ? <AddImage source={{ uri: image }} /> : null}
<InputField
placeholder="¿Qué tienes en mente?"
multiline
numberOfLines={4}
value={post}
onChangeText={(content) => setPost(content)}
/>
{uploading ? (
<StatusWrapper>
<Text>{transferred} % Completed!</Text>
<ActivityIndicator size="large" color="#27AE60" />
</StatusWrapper>
) : (
<SubmitBtn onPress={submitPost}>
<SubmitBtnText>Post</SubmitBtnText>
</SubmitBtn>
)}
</InputWrapper>
<ActionButton buttonColor="rgb(26, 188, 156)">
<ActionButton.Item
buttonColor='#9b59b6'
title="New Task" onPress={() => console.log("notes tapped!")}>
<Icon name="md-create" style={globalStyles.actionButtonIcon} />
</ActionButton.Item>
<ActionButton.Item
buttonColor='#3498db'
title="Take Photp"
onPress={takePhotoFromCamera}>
<Icon name="camera-outline" style={globalStyles.actionButtonIcon} />
</ActionButton.Item>
<ActionButton.Item
buttonColor='#1abc9c'
title="Elegir"
onPress={choosePhotoFromLibrary}>
<Icon name="md-images-outline" style={globalStyles.actionButtonIcon} />
</ActionButton.Item>
</ActionButton>
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
)
}
export default AddPostScreen
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}})
export default KeyboardAvoidingComponent
CODE:
import React, { useContext, useState } from "react"
import { View, Text, Button, Alert, ActivityIndicator, ScrollView } from "react-native"
import ActionButton from "react-native-action-button"
import Icon from 'react-native-vector-icons/Ionicons'
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'
import ImagePicker from "react-native-image-crop-picker"
import storage from '#react-native-firebase/storage'
import firestore from '#react-native-firebase/firestore'
import globalStyles from "../styles/global"
import {
InputField,
InputWrapper,
AddImage,
SubmitBtn,
SubmitBtnText,
StatusWrapper
} from '../styles/AddPostStyles'
import { AuthContext } from "../navigation/AuthProvider"
const AddPostScreen = () => {
const { user, logout } = useContext(AuthContext)
const [image, setImage] = useState(null)
const [uploading, setUploading] = useState(false)
const [transferred, setTransferred] = useState(0)
const [post, setPost] = useState(null)
const takePhotoFromCamera = () => {
ImagePicker.openCamera({
width: 1200,
height: 780,
cropping: true,
}).then((image) => {
console.log(image)
const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path
setImage(imageUri)
})
}
const choosePhotoFromLibrary = () => {
ImagePicker.openPicker({
width: 1200,
height: 780,
cropping: true,
}).then((image) => {
console.log(image)
const imageUri = Platform.OS === 'ios' ? image.sourceURL : image.path
setImage(imageUri)
})
}
const submitPost = async () => {
const imageUrl = await uploadImage()
console.log('Image Url', imageUrl)
firestore()
.collection('posts')
.add({
userId: user.uid,
post: post,
postImg: imageUrl,
postTime: firestore.Timestamp.fromDate(new Date()),
likes: null,
comments: null
})
.then(() => {
console.log('Post Added...')
Alert.alert(
'Post published!',
'Your post has been published Successfully!',
)
setPost(null)
})
.catch((error) => {
console.log('Something went wrong with added post to firestore.', error)
})
}
const uploadImage = async () => {
if (image == null) {
return null
}
const uploadUri = image
let filename = uploadUri.substring(uploadUri.lastIndexOf('/') + 1)
// Add timestad to File Name
const extension = filename.split('.').pop()
const name = filename.split('.').slice(0, -1).join('.')
filename = name + Date.now() + '.' + extension
setUploading(true)
setTransferred(0)
const storageRef = storage().ref(`photos/${filename}`)
const task = storageRef.putFile(uploadUri)
// Set transferred state
task.on('state_changed', (taskSnapshot) => {
console.log(`${taskSnapshot.bytesTransferred} transferred out of ${taskSnapshot.totalBytes}`)
setTransferred(
Math.round(taskSnapshot.bytesTransferred / taskSnapshot.totalBytes) * 100
)
})
try {
await task
const url = await storageRef.getDownloadURL()
setUploading(false)
setImage(null)
/* Alert.alert(
'Imagen subida!',
'Tu imagen se subio correctamente!',
) */
return url
} catch (e) {
console.log(e)
return null
}
}
return (
<View style={globalStyles.container}>
<InputWrapper>
{image != null ? <AddImage source={{ uri: image }} /> : null}
<InputField
placeholder="¿Qué tienes en mente?"
multiline
numberOfLines={4}
value={post}
onChangeText={(content) => setPost(content)}
/>
{uploading ? (
<StatusWrapper>
<Text>{transferred} % Completed!</Text>
<ActivityIndicator size="large" color="#27AE60" />
</StatusWrapper>
) : (
<SubmitBtn onPress={submitPost}>
<SubmitBtnText>Post</SubmitBtnText>
</SubmitBtn>
)}
</InputWrapper>
<ActionButton buttonColor="rgb(26, 188, 156)">
<ActionButton.Item
buttonColor='#9b59b6'
title="New Task" onPress={() => console.log("notes tapped!")}>
<Icon name="md-create" style={globalStyles.actionButtonIcon} />
</ActionButton.Item>
<ActionButton.Item
buttonColor='#3498db'
title="Take Photp"
onPress={takePhotoFromCamera}>
<Icon name="camera-outline" style={globalStyles.actionButtonIcon} />
</ActionButton.Item>
<ActionButton.Item
buttonColor='#1abc9c'
title="Elegir"
onPress={choosePhotoFromLibrary}>
<Icon name="md-images-outline" style={globalStyles.actionButtonIcon} />
</ActionButton.Item>
</ActionButton>
</View>
)
}
export default AddPostScreen
//////////////////////////
import styled from 'styled-components'
export const InputWrapper = styled.View`
flex: 1;
justify-content: center;
align-items: center;
width: 100%;
background-color: #2e64e515;
`
export const InputField = styled.TextInput`
justify-content: center;
align-items: center;
font-size: 24px;
text-align: center;
width:90%;
margin-bottom: 15px;
`
export const AddImage = styled.Image`
width: 100%;
height: 250px;
margin-bottom: 15px;
`
export const StatusWrapper = styled.View`
justify-content: center;
align-items: center;
`
export const SubmitBtn = styled.TouchableOpacity`
flex-direction: row;
justify-content: center;
background-color: #2e64e515;
border-radius: 5px;
padding: 10px 25px;
`
export const SubmitBtnText = styled.Text`
font-size: 18px;
font-family: 'Lato-Bold';
font-weight: bold;
color: #2e64e5;
`
you should use react-native-keyboard-aware-scroll-view
yarn add react-native-keyboard-aware-scroll-view
it will resolve your issue.
Usage
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
<KeyboardAwareScrollView>
<View>
<TextInput />
</View>
</KeyboardAwareScrollView>
Note: if you are using react-native<65 then you should use react-native-keyboard-aware-scroll-view#0.9.4
Ok I think I found you a solution. Follow the previous steps I mentioned which are:
yarn add react-native-keyboard-aware-scroll-view
import { useRef } from 'react';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
const scrollViewRef = useRef();
Use the KeyboardAwareScrollView and set its ref you just declared.
<KeyboardAwareScrollView ref={scrollViewRef} >
<View style={{flex: 1}} >
//Content
</View>
</KeyboardAwareScrollView>
With your TextInput (InputField in your case), make sure to scroll to the end every time the text is changed.
<TextInput
onChangeText={text => {
setText(text);
scrollViewRef.current.scrollToEnd({animated:true});
}
/>
That should work. If not here's a snack project I created that shows it working https://snack.expo.dev/#bgcodes/7ff849
I used native base code of drawer to make a sidebar for my home page, the button works fine I used on press as this.openDrawer(). I am using this code :
import React, { Component } from 'react';
import { Drawer, Header, Icon, Left, Body, Right } from 'native-base';
import { Text, View, StatusBar } from 'react-native';
import SideBar from '../screens/sidebar';
export default class home extends Component {
closeDrawer = () => {
this.drawer._root.close()
};
openDrawer = () => {
this.drawer._root.open()
};
render() {
return (
<Drawer
ref={(ref) => { this.drawer = ref; }}
content={<SideBar navigator={this.navigator} />}
onClose={() => this.closeDrawer()} >
<Header>
<Left>
<Icon name="menu" style={{ color: '#fff' }}
onPress={() => this.openDrawer()}
/>
</Left>
<Body style={{ alignItems: 'center' }}>
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold' }}>Welcome to app!</Text>
</Body>
<Right></Right>
</Header>
</Drawer>
);
}
}
Sidebar opacity is less. check this image
I wonder why its opacity is less, I have tried giving a background color to white in my sidebar screen. Please help , anyone with native base?
I'm building an app with React-Native.
I'm trying to render a URL with a Webview, even some basic HTML or a site like m.facebook.com will not render in iOS. Tried different solution like other Webviews but none give results.
When in Android i don't have any problems and the page(s) will render just fine. Am i missing some key information.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actions } from 'react-native-navigation-redux-helpers';
import { Container, Header, Title, Content, Button, Icon, Footer } from 'native-base';
import { Platform, WebView } from 'react-native';
import FooterTabs from '../../components/footerTabs/FooterTabs';
import { openDrawer } from '../../actions/drawer';
import { setWebsiteUrl } from '../../actions/website';
import styles from './styles';
const {
pushRoute,
popRoute,
} = actions;
class Website extends Component {
constructor(props, context) {
super(props, context);
this.state = {};
this.openFrame = this.openFrame.bind(this);
}
popRoute() {
this.props.popRoute(this.props.navigation.key);
}
pushRoute(route, index) {
this.props.pushRoute({ key: route, index: 1 }, this.props.navigation.key);
}
openFrame(url, name) {
this.props.setWebsiteUrl(url, name);
this.pushRoute('website', 2);
}
render() {
const { props } = this;
const { website, totalQuantity } = props;
const { frameUrl, frameName } = website;
return (
<Container style={styles.container} theme={deenTheme}>
<Header toolbarDefaultBg="#FFF" toolbarTextColor="FBFAFA">
<Button transparent onPress={this.props.openDrawer}>
<Icon name="ios-menu" />
</Button>
<Title style={styles.headerText}>{frameName}</Title>
<Button transparent onPress={() => this.openFrame('/cart', 'Winkelwagen')}>
<Icon style={{ fontSize: 25 }} name="md-basket" />
</Button>
<Button transparent>
<Icon style={{ fontSize: 25 }} name="md-search" />
</Button>
</Header>
<Content>
<WebView
source={{ uri: frameUrl }}
startInLoadingState
javaScriptEnabledAndroid
javaScriptEnabled
domStorageEnabled
scrollEnabled
style={{ flex: 1, width: 320 }}
/>
</Content>
<Footer theme={deenTheme}>
<FooterTabs />
</Footer>
</Container>
);
}
}
Website.propTypes = {
totalQuantity: React.PropTypes.number,
openDrawer: React.PropTypes.func,
setWebsiteUrl: React.PropTypes.func,
popRoute: React.PropTypes.func,
pushRoute: React.PropTypes.func,
navigation: React.PropTypes.shape({
key: React.PropTypes.string,
}),
};
function bindAction(dispatch) {
return {
openDrawer: () => dispatch(openDrawer()),
popRoute: key => dispatch(popRoute(key)),
pushRoute: (route, key) => dispatch(pushRoute(route, key)),
setWebsiteUrl: (url, name) => dispatch(setWebsiteUrl(url, name)),
};
}
const mapStateToProps = state => ({
navigation: state.cardNavigation,
website: state.website,
totalQuantity: state.cart.totalQuantity,
});
export default connect(mapStateToProps, bindAction)(Website);
UPDATE!
in iOS i need to configure style width & height else it won't work.
I want to make a gender selection using picker. but the mode is not working in ios. Im not sure in android.
<Picker
style={{justifyContent: 'center',backgroundColor: 'white'}}
selectedValue={this.state.gender}
onValueChange={(gender) => this.setState({ gender })}
mode='dialog'
>
<Item label="Male" value="Male" />
<Item label="Female" value="Female" />
</Picker>
Hope anyone could give some suggestion..
Thank you
The <Picker> component renders as a UIPickerView on iOS - that's what you're seeing there. This picker is rendered in-place rather than in a modal (as is the case on Android) - the mode prop you specified only applies to Android.
There are two options: render the picker within something like a <Modal> or use a different component entirely.
I solved this type of issue by doing like this.
import PickerExample from './PickerExample.js';
class Sample extends React.Component {
constructor(props) {
super(props);
this.state = {gender: '',};
this.updateGender = this.updateGender.bind(this);
}
updateGender(val){
this.setState({
gender: val
},function(){
alert("callback function as your wish")
});
}
render(){
return (
<PickerExample gender={this.state.gender} updateGender={this.updateGender.bind(this)} />
);
}
The PickerExample.js file look like this.
export default PickerExample = (props) => {
return (
<Picker selectedValue = {props.gender} onValueChange = {props.updateGender}>
<Picker.Item label = "MALE" value ="Male" />
<Picker.Item label = "Female" value = "female" />
</Picker>
);
}
I use the .ios.jsx extension for the iOS specific picker. Which I code as follows:
export const SelectWidget = ({
selectedOption,
setSelectedOption,
placeholderLabel,
options
}) => {
const items = options.map((option) => {
return <Picker.Item
key={option.key ?? option.label}
label={option.label}
value={option.value}
/>;
});
return (
<Picker
onValueChange={(value) => {
setSelectedOption(value);
}}
placeholder={{
label: placeholderLabel,
value: null,
}}
selectedValue={selectedOption}
style={{
width: '100%', // fill up the width of the device (without this, nothing gets displayed
}}
itemStyle={{
height: 150, // this reduces the height of the selector
color: colors.text, // if you're in dark mode set this to white.
}}
>
// default item
<Picker.Item label={placeholderLabel} value={null} />
{items}
</Picker>
);
};
I want to display a navigation bar on top of the screen using React's Navigator and this custom NavigationBar component.
I am using the following code, but for some reason the navigation bar renders at the bottom of the screen:
import NavigationBar from 'react-native-navbar'
class MyDebts extends Component {
constructor(props) {
super(props)
this.state = {
selectedTab: 'persons'
}
}
_renderScene(route, navigator) {
console.log('render scene with route: ', route)
return <route.component route={route} navigator={navigator} />
}
render() {
return (
<Provider store={store}>
<TabBarIOS selectedTab={this.state.selectedTab}>
<TabBarIOS.Item
selected={this.state.selectedTab === 'persons'}
title='Persons'
onPress={() => {
this.setState({
selectedTab: 'persons',
})
}}>
<Navigator
initialRoute={{ name: 'Persons', component: PersonsRootComponent }}
renderScene={this._renderScene}
navigationBar={<NavigationBar style={styles.nav} title={{title: 'Persons'}}/>}
/>
</TabBarIOS.Item>
</TabBarIOS>
</Provider>
)
}
}
const styles = StyleSheet.create({
nav: {
height: 160,
backgroundColor: 'red'
}
})
The PersonsRootComponent renders properly, but I have no idea why the NavigationBar is placed on the bottom of the screen...
If you want to put the navbar somewhere else other than the bottom, you need absolute positioning on the navbar.
container: {
width: width,
flexDirection: 'row',
position:'absolute',
top:0,
left:0
},