How avoid to press button on scroll React native - react-native

I have two scrollviews with Pressable inside but when I scroll inside the scrollview it triggers the pressable button.
I'd like to avoid trigger that pressable on scroll but only on press as it should be.
Here you can see what I mean :
https://imgur.com/zh6Jch8
Sorry we can't see the mouse, I don't know how to show it with Android studio.
I've implemented onLongPress with Pressable but it's not really great as it results in worse user experience.
No code provided here : I just use Pressable with on onLongPress for now inside scrollview or Flatlist

The press is visible(button gets a bit transparent), however the onPress function is not called. Also try and use TouchableOpacity instead of Pressable and see if it works.
render() {
const time = [{id: 0}, {id: 0}, {id: 0}, {id: 0}, {id: 0}];
return (
<ScrollView style={{flex: 1, backgroundColor: 'red', marginTop: 50}}>
{time.map((data, index) => {
return (
<TouchableOpacity
style={{
paddingVertical: 100,
marginBottom: 20,
backgroundColor: 'green',
}}
onPress={() => alert(`HEllo ${index}`)}>
<Text>HEllo ${data.id}</Text>
</TouchableOpacity>
);
})}
</ScrollView>
);
}
I tried the above and the alert(HEllo ${index}) was never called.
Also if you want to prevent the user from seeing any interaction of buttonPress, try using <TouchableWithutFeedback /> from react-native

Here's what I did.
use onPressIn and onPressOut to modify the style of my pressable through a "pressed" state.
use onPress for my action, use onLongPress to do absolutely nothing so I can scroll without activating my action.
<Pressable
onPressIn={() => {
setPressed(true)
}}
onPressOut={() => {
setPressed(false)
}}
onPress={() => {
whateverYouWantYourPressableToDo()
}}
onLongPress={() => {}}
>
I just tested it on my own app, and it seems to be working as expected. When I tap a button my action triggers. when I press and hold for scrolling, I scroll and my pressable does nothing. Hope this is useful for you, or someone in the future :)

I spent my whole day on this problem but finally I found a solution for this problem.
import it from gesture handler either TouchableWithoutFeedback or touchableOpacity.
import { TouchableWithoutFeedback } from "react-native-gesture-handler";
it will definitely work and your problem will solve.

Related

Vertically align Pressable inside a Text component

<View
style={{
flexDirection: "row",
}}
>
<Text
style={{
flex: 1,
}}
>
By continuing, you agree to our{" "}
<Pressable
onPress={...}
>
<Text>
Terms of Service
</Text>
</Pressable>
</Text>
</View>
"Terms of Service" is printed higher than "By continuing, you agree to our". How do I vertically align them?
Or more precisely - how do I get the Pressable Text to vertically align to the bottom?
This is a bug in React Native itself. There are several open reports of this bug on React Native's GitHub, but the chances of it being fixed don't look good:
https://github.com/facebook/react-native/issues/30375 - for the general problem of Views nested in Text being mis-aligned on Android, and a core contributor responded, but appeared to get derailed and stuck in some details specific to mimicking superscript and subscript.
https://github.com/facebook/react-native/issues/31955 - specific to Pressable and someone posted a PR to fix it, but Facebook closed it because no-one from Facebook got around to reviewing it before it became stale and out of date with the main branch.
There's also some discussion in this issue comment, but the issue got closed.
In React Native >= 0.65, if your inline pressable element uses only text styles, you can work around this issue by using <Text> with onPress (and onPressIn and onPressOut to style the pressed state). Crude example:
/**
* Like a simplified Pressable that doesn't look broken inline in `Text` on Android
*/
const TextButton = ({ children, onPress, style, ...rest } => {
const [pressed, setPressed] = useState(false)
const onPressIn = () => setPressed(true)
const onPressOut = () => setPressed(false)
return (
<Text
onPress={onPress}
onPressIn={onPressIn}
onPressOut={onPressOut}
style={typeof style === 'function' ? style({ pressed }) : style}
{...rest}
>
{typeof children === 'function' ? children({ pressed }) : children}
</Text>
)
}
Beware, however, that there are also bugs around selecting interactive elements nested inside text using accessibility tools. If you can simply avoid nesting the interactive element in text, and have it on its own line, that's probably better: link-like interactive nested text isn't well supported in React Native currently.
On older versions of React Native before 0.65, Text didn't support onPressIn or onPressOut:
If you don't need Pressable's pressed state, use Text instead of Pressable (as the asker did: https://stackoverflow.com/a/66590787/568458)
If you do need pressed state, Text doesn't support the required onPressIn/Out handlers. However, TouchableWithoutFeedback does support those, and works by injecting props into its child so the Text will remain Text with no wrapping View. Wrap a Text in TouchableWithoutFeedback and pass the touchable onPress with onPressIn and onPressOut handlers and store the pressed state yourself.
/**
* Like a simplified Pressable that doesn't look broken inline in `Text` on Android
*/
const TextButton = ({ children, onPress, style, ...rest } => {
const [pressed, setPressed] = useState(false)
const onPressIn = () => setPressed(true)
const onPressOut = () => setPressed(false)
// TouchableWithoutFeedback modifies and returns its child; this returns `Text`
// plus press in/out events attached that aren't supported by Text directly.
return (
<TouchableWithoutFeedback
onPress={onPress}
onPressIn={onPressIn}
onPressOut={onPressOut}
>
<Text
style={typeof style === 'function' ? style({ pressed }) : style}
{...rest}
>
{typeof children === 'function' ? children({ pressed }) : children}
</Text>
</TouchableWithoutFeedback>
)
}
Warning: if you're also using React Native Web and React Navigation, don't use the TouchableWithoutFeedback approach on Web, use pure Pressable on web, because React Navigation's navigate functions don't reliably work when passed to Touchable*s on Web due to a quirk of how the event handlers are set up (but they do work in Pressable), and this issue doesn't exist on Web.
Ended up doing this differently, using the onPress property of the <Text> component and finally wrapping all <Text> components in another <Text> component to have a proper line break:
<View>
<Text>
<Text>
By continuing, you agree to our{" "}
</Text>
<Text onPress={() => {...}}>
Terms of Service
</Text>
<Text>
{" "}and our{" "}
</Text>
<Text onPress={() => {...}}>
Privacy Policy
</Text>
</Text>
</View>
The snippet below should work but hard to understand without giving a shot. If you can provide screenshots I can help more in sake of getting a more proper result.
<View>
<Text style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
}}>
By continuing, you agree to our{" "}
<Pressable
onPress={() => {
navigate("LegalStack", { screen: "Terms" });
}}
>
<Text style={{margin: 'auto'}}>
Terms of Service
</Text>
</Pressable>
</Text>
</View>
I found a very hackidy-hack solution...
<Text selectable={true}>
<Text>if you click</Text>
<TouchableOpacity
style={{flexDirection: 'row'}}
onPress={() => Linking.openURL("https://www.google.com")}
>
<Text
style={{
alignSelf: 'flex-end',
marginBottom: -4,
}}
>
here
</Text>
</TouchableOpacity>
<Text>, it will open google</Text>
</Text>
By default the flexDirection is column. Change it to flexDirection:"row"

Is that possible to force keyboard gone immediately in React Native? [duplicate]

If I tap onto a textinput, I want to be able to tap somewhere else in order to dismiss the keyboard again (not the return key though). I haven't found the slightest piece of information concerning this in all the tutorials and blog posts that I read.
This basic example is still not working for me with react-native 0.4.2 in the Simulator. Couldn't try it on my iPhone yet.
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onEndEditing={this.clearFocus}
/>
</View>
The problem with keyboard not dismissing gets more severe if you have keyboardType='numeric', as there is no way to dismiss it.
Replacing View with ScrollView is not a correct solution, as if you have multiple textInputs or buttons, tapping on them while the keyboard is up will only dismiss the keyboard.
Correct way is to encapsulate View with TouchableWithoutFeedback and calling Keyboard.dismiss()
EDIT: You can now use ScrollView with keyboardShouldPersistTaps='handled' to only dismiss the keyboard when the tap is not handled by the children (ie. tapping on other textInputs or buttons)
If you have
<View style={{flex: 1}}>
<TextInput keyboardType='numeric'/>
</View>
Change it to
<ScrollView contentContainerStyle={{flexGrow: 1}}
keyboardShouldPersistTaps='handled'
>
<TextInput keyboardType='numeric'/>
</ScrollView>
or
import {Keyboard} from 'react-native'
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={{flex: 1}}>
<TextInput keyboardType='numeric'/>
</View>
</TouchableWithoutFeedback>
EDIT: You can also create a Higher Order Component to dismiss the keyboard.
import React from 'react';
import { TouchableWithoutFeedback, Keyboard, View } from 'react-native';
const DismissKeyboardHOC = (Comp) => {
return ({ children, ...props }) => (
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<Comp {...props}>
{children}
</Comp>
</TouchableWithoutFeedback>
);
};
const DismissKeyboardView = DismissKeyboardHOC(View)
Simply use it like this
...
render() {
<DismissKeyboardView>
<TextInput keyboardType='numeric'/>
</DismissKeyboardView>
}
NOTE: the accessible={false} is required to make the input form continue to be accessible through VoiceOver. Visually impaired people will thank you!
This just got updated and documented! No more hidden tricks.
import { Keyboard } from 'react-native'
// Hide that keyboard!
Keyboard.dismiss()
Github link
Use React Native's Keyboard.dismiss()
Updated Answer
React Native exposed the static dismiss() method on the Keyboard, so the updated method is:
import { Keyboard } from 'react-native';
Keyboard.dismiss()
Original Answer
Use React Native's dismissKeyboard Library.
I had a very similar problem and felt like I was the only one that didn't get it.
ScrollViews
If you have a ScrollView, or anything that inherits from it like a ListView, you can add a prop that will automatically dismiss the keyboard based on press or dragging events.
The prop is keyboardDismissMode and can have a value of none, interactive or on-drag. You can read more on that here.
Regular Views
If you have something other than a ScrollView and you'd like any presses to dismiss the keyboard, you can use a simple TouchableWithoutFeedback and have the onPress use React Native's utility library dismissKeyboard to dismiss the keyboard for you.
In your example, you could do something like this:
var DismissKeyboard = require('dismissKeyboard'); // Require React Native's utility library.
// Wrap your view with a TouchableWithoutFeedback component like so.
<View style={styles.container}>
<TouchableWithoutFeedback onPress={ () => { DismissKeyboard() } }>
<View>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
<TextInput style={{height: 40, borderColor: 'gray', borderWidth: 1}} />
</View>
</TouchableWithoutFeedback>
</View>
Note: TouchableWithoutFeedback can only have a single child so you need to wrap everything below it in a single View as shown above.
use this for custom dismissal
var dismissKeyboard = require('dismissKeyboard');
var TestView = React.createClass({
render: function(){
return (
<TouchableWithoutFeedback
onPress={dismissKeyboard}>
<View />
</TouchableWithoutFeedback>
)
}
})
The simple answer is to use a ScrollView instead of View and set the scrollable property to false (might need to adjust some styling though).
This way, the keyboard gets dismissed the moment I tap somewhere else. This might be an issue with react-native, but tap events only seem to be handled with ScrollViews which leads to the described behaviour.
Edit: Thanks to jllodra. Please note that if you tap directly into another Textinput and then outside, the keyboard still won't hide.
You can import keyboard from react-native like below:
import { Keyboard } from 'react-native';
and in your code could be something like this:
render() {
return (
<TextInput
onSubmit={Keyboard.dismiss}
/>
);
}
static dismiss()
Dismisses the active keyboard and removes focus.
I'm brand new to React, and ran into the exact same issue while making a demo app. If you use the onStartShouldSetResponder prop (described here), you can grab touches on a plain old React.View. Curious to hear more experienced React-ers' thoughts on this strategy / if there's a better one, but this is what worked for me:
containerTouched(event) {
this.refs.textInput.blur();
return false;
}
render() {
<View onStartShouldSetResponder={this.containerTouched.bind(this)}>
<TextInput ref='textInput' />
</View>
}
2 things to note here. First, as discussed here, there's not yet a way to end editing of all subviews, so we have to refer to the TextInput directly to blur it. Second, the onStartShouldSetResponder is intercepted by other touchable controls on top of it. So clicking on a TouchableHighlight etc (including another TextInput) within the container view will not trigger the event. However, clicking on an Image within the container view will still dismiss the keyboard.
Use ScrollView instead of View and set the keyboardShouldPersistTaps attribute to false.
<ScrollView style={styles.container} keyboardShouldPersistTaps={false}>
<TextInput
placeholder="Post Title"
onChange={(event) => this.updateTitle(event.nativeEvent.text)}
style={styles.default}/>
</ScrollView>
Wrapping your components in a TouchableWithoutFeedback can cause some weird scroll behavior and other issues. I prefer to wrap my topmost app in a View with the onStartShouldSetResponder property filled in. This will allow me to handle all unhandled touches and then dismiss the keyboard. Importantly, since the handler function returns false the touch event is propagated up like normal.
handleUnhandledTouches(){
Keyboard.dismiss
return false;
}
render(){
<View style={{ flex: 1 }} onStartShouldSetResponder={this.handleUnhandledTouches}>
<MyApp>
</View>
}
The simplest way to do this
import {Keyboard} from 'react-native'
and then use the function Keyboard.dismiss()
That's all.
Here is a screenshot of my code so you can understand faster.
Now wrap the entire view with TouchableWithoutFeedback and onPress function is keyboard.dismiss()
Here is the example
In this way if user tap on anywhere of the screen excluding textInput field, keyboard will be dismissed.
There are a few ways,
if you control of event like onPress you can use:
import { Keyboard } from 'react-native'
onClickFunction = () => {
Keyboard.dismiss()
}
if you want to close the keyboard when the use scrolling:
<ScrollView keyboardDismissMode={'on-drag'}>
//content
</ScrollView>
More option is when the user clicks outside the keyboard:
<KeyboardAvoidingView behavior='padding' style={{ flex: 1}}>
//inputs and other content
</KeyboardAvoidingView>
If any one needs a working example of how to dismiss a multiline text input here ya go! Hope this helps some folks out there, the docs do not describe a way to dismiss a multiline input at all, at least there was no specific reference on how to do it. Still a noob to actually posting here on the stack, if anyone thinks this should be a reference to the actual post this snippet was written for let me know.
import React, { Component } from 'react'
import {
Keyboard,
TextInput,
TouchableOpacity,
View,
KeyboardAvoidingView,
} from 'react-native'
class App extends Component {
constructor(props) {
super(props)
this.state = {
behavior: 'position',
}
this._keyboardDismiss = this._keyboardDismiss.bind(this)
}
componentWillMount() {
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
componentWillUnmount() {
this.keyboardDidHideListener.remove()
}
_keyboardDidHide() {
Keyboard.dismiss()
}
render() {
return (
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={this.state.behavior}
>
<TouchableOpacity onPress={this._keyboardDidHide}>
<View>
<TextInput
style={{
color: '#000000',
paddingLeft: 15,
paddingTop: 10,
fontSize: 18,
}}
multiline={true}
textStyle={{ fontSize: '20', fontFamily: 'Montserrat-Medium' }}
placeholder="Share your Success..."
value={this.state.text}
underlineColorAndroid="transparent"
returnKeyType={'default'}
/>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
)
}
}
Updated usage of ScrollView for React Native 0.39
<ScrollView scrollEnabled={false} contentContainerStyle={{flex: 1}} />
Although, there is still a problem with two TextInput boxes. eg. A username and password form would now dismiss the keyboard when switching between inputs. Would love to get some suggestions to keep keyboard alive when switching between TextInputs while using a ScrollView.
const dismissKeyboard = require('dismissKeyboard');
dismissKeyboard(); //dismisses it
Approach No# 2;
Thanks to user #ricardo-stuven for pointing this out, there is another better way to dismiss the keyboard which you can see in the example in the react native docs.
Simple import Keyboard and call it's method dismiss()
I just tested this using the latest React Native version (0.4.2), and the keyboard is dismissed when you tap elsewhere.
And FYI: you can set a callback function to be executed when you dismiss the keyboard by assigning it to the "onEndEditing" prop.
If i'm not mistaken the latest version of React Native has solved this issue of being able to dismiss the keyboard by tapping out.
How about placing a touchable component around/beside the TextInput?
var INPUTREF = 'MyTextInput';
class TestKb extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={{ flex: 1, flexDirection: 'column', backgroundColor: 'blue' }}>
<View>
<TextInput ref={'MyTextInput'}
style={{
height: 40,
borderWidth: 1,
backgroundColor: 'grey'
}} ></TextInput>
</View>
<TouchableWithoutFeedback onPress={() => this.refs[INPUTREF].blur()}>
<View
style={{
flex: 1,
flexDirection: 'column',
backgroundColor: 'green'
}}
/>
</TouchableWithoutFeedback>
</View>
)
}
}
Wrap your whole component with:
import { TouchableWithoutFeedback, Keyboard } from 'react-native'
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
...
</TouchableWithoutFeedback>
Worked for me
Using KeyBoard API from react-native does the trick.
import { Keyboard } from 'react-native'
// Hide the keyboard whenever you want using !
Keyboard.dismiss()
Keyboard module is used to control keyboard events.
import { Keyboard } from 'react-native'
Add below code in render method.
render() {
return <TextInput onSubmitEditing={Keyboard.dismiss} />;
}
You can use -
Keyboard.dismiss()
static dismiss() Dismisses the active keyboard and removes focus as per react native documents.
https://facebook.github.io/react-native/docs/keyboard.html
Use
Keyboard.dismiss(0);
to hide the keyboard.
Using keyboardShouldPersistTaps in the ScrollView you can pass in "handled", which deals with the issues that people are saying comes with using the ScrollView. This is what the documentation says about using 'handled': the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor). Here is where it's referenced.
First import Keyboard
import { Keyboard } from 'react-native'
Then inside your TextInput you add Keyboard.dismiss to the onSubmitEditing prop. You should have something that looks like this:
render(){
return(
<View>
<TextInput
onSubmitEditing={Keyboard.dismiss}
/>
</View>
)
}
We can use keyboard and tochablewithoutfeedback from react-native
const DismissKeyboard = ({ children }) => (
<TouchableWithoutFeedback
onPress={() => Keyboard.dismiss()}
>
{children}
</TouchableWithoutFeedback>
);
And use it in this way:
const App = () => (
<DismissKeyboard>
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="username"
keyboardType="numeric"
/>
<TextInput
style={styles.input}
placeholder="password"
/>
</View>
</DismissKeyboard>
);
I also explained here with source code.
Use Keyboard.dismiss() to dismiss keyboard at any time.
Wrap the View component that is the parent of the TextInput in a Pressable component and then pass Keyboard. dismiss to the onPress prop. So, if the user taps anywhere outside the TextInput field, it will trigger Keyboard. dismiss, resulting in the TextInput field losing focus and the keyboard being hidden.
<Pressable onPress={Keyboard.dismiss}>
<View>
<TextInput
multiline={true}
onChangeText={onChangeText}
value={text}
placeholder={...}
/>
</View>
</Pressable>
in ScrollView use
keyboardShouldPersistTaps="handled"
This will do your job.
There are many ways you could handle this, the answers above don't include returnType as it was not included in react-native that time.
1: You can solve it by wrapping your components inside ScrollView, by default ScrollView closes the keyboard if we press somewhere. But incase you want to use ScrollView but disable this effect. you can use pointerEvent prop to scrollView
pointerEvents = 'none'.
2: If you want to close the keyboard on a button press, You can just use Keyboard from react-native
import { Keyboard } from 'react-native'
and inside onPress of that button, you can useKeyboard.dismiss()'.
3: You can also close the keyboard when you click the return key on the keyboard,
NOTE: if your keyboard type is numeric, you won't have a return key.
So, you can enable it by giving it a prop, returnKeyType to done.
or you could use onSubmitEditing={Keyboard.dismiss},It gets called whenever we press the return key. And if you want to dismiss the keyboard when losing focus, you can use onBlur prop, onBlur = {Keyboard.dismiss}
Keyboard.dismiss() will do it. But sometimes it may lose the focus and Keyboard will be unable to find the ref. The most consistent way to do is put a ref=_ref to the textInput, and do _ref.blur() when you need to dismiss, and _ref.focus() when you need to bring back the keyboard.
Here is my solution for Keyboard dismissing and scrolling to tapped TextInput (I am using ScrollView with keyboardDismissMode prop):
import React from 'react';
import {
Platform,
KeyboardAvoidingView,
ScrollView
} from 'react-native';
const DismissKeyboard = ({ children }) => {
const isAndroid = Platform.OS === 'android';
const behavior = isAndroid ? false : 'padding';
return (
<KeyboardAvoidingView
enabled
behavior={ behavior }
style={{ flex: 1}}
>
<ScrollView
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
>
{ children }
</ScrollView>
</KeyboardAvoidingView>
);
};
export default DismissKeyboard;
usage:
render(){
return(
<DismissKeyboard>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
</DismissKeyboard>
);
}

Is there a Cross platform clearButtonMode for Android with React Native

How should one implement the "X" to clear button in react native so that it works with Android as well as iOS. iOS has the text input option of "clearButtonMode" enum('never', 'while-editing', 'unless-editing', 'always').
To make it cross platform, do we need to just add an android conditional rendering of the clear button? Something like:
{Platform.OS === 'android' && <ClearTextButton />}
Seems a bit hacky so I am wondering if there is a cleaner method for this.
For your problem, you just need to create a simple button to handle the clear function of your input field and place it right next to your TextInput component to have the effect of clearButtonMode.
A naive implementation could be something like this:
Create these states in your main component constructor :
A state for the status of your TextInput (is it touched?, does it have text yet?)
A state for the actual value of your TextInput, set your TextInput's value to this state.
For example:
this.state = {
textInput1Status: 'untouched',
textInput1Value: '',
};
Create callback functions to set your states:
Create a callback function to set both your TextInput's value state and status state and assign it to the onChange prop of you TextInput.
For example:
<TextInput
onChangeText={(text) => this.onTextInput1Change(text)}
value={this.state.textInput1Value}
/>
...
onTextInput1Change(text) {
this.setState({
textInput1Status: 'touched',
textInput1Value: text
});
}
Create your own button using TouchableOpacity and handle the clear function.
For example:
<TouchableOpacity onPress={this.clearText}>
<Image
style={styles.button}
source={require('./myButton.png')}
/>
</TouchableOpacity>
...
clearText() {
this.setState({
textInput1Status: 'untouched',
textInput1Value: '',
});
}
Handle the rendering of your "X" button:
For example:
renderClearButotn() {
if (this.state.textInput1Status == 'touched') {
return (
<TouchableOpacity onPress={this.clearText}>
<Image
style={styles.button}
source={require('./myButton.png')}
/>
</TouchableOpacity>
);
} else {
return '';
}
}
...
render() {
return (
<TextInput
onChangeText={(text) => this.onTextInput1Change(text)}
value={this.state.textInput1Value}
/>
{this.renderClearButton()}
);
}
In this way your code will be independent from both iOS and Android. I hope this could help you!
There is another simple solution I found from this article. It works perfect for me in Android, and it is expected to give the same view and behavior in iOS also.
I had to modify the styles slightly to match with my UI
closeButtonParent: {
justifyContent: 'center',
alignItems: 'center',
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
backgroundColor: "#cdcdcd",
width: 30,
},
Code credit goes to https://www.codevscolor.com/react-native-text-input-clear-button/ auther
This solution works ok but it's not the exact same effect than the clearButtonMode in iOS. The clearButtonMode won't dismiss the keyboard when clicked, and this solution for android will dispatch the Keyboard.dismiss event natively and there's no way to catch it, so the user needs to tap again on the input to get the keyboard back.

Dismiss keyboard in multiline TextInput in React native

When the user presses the Return key in a multiline TextInput, a new line is created and the keyboard continues to be visible. How should the keyboard be dismissed for multiline TextInput in React native?
I did some research. I found that clicking on the View outside the TextInput does not blur the TextInput, which is causing the keyboard to remain visible.
<View style={styles.container}>
<TextInput
placeholder="To"
style={styles.input}
value={this.state.to}
onChangeText={(to) => this.setState({to})}
/>
<TextInput
placeholder="Text"
style={styles.textarea}
multiline={true}
numberOfLines={4}
value={this.state.text}
onChangeText={(text) => this.setState({text})}
/>
</View>
For ScrollView, there is a prop - keyboardShouldPersistTaps which causes the TextInput to blur. Is there any equivalent of that for View? I want the multiline TextInput to blur so that the keyboard gets dismissed.
TextInput has a blurOnSubmit prop; when set to true, the return key dismisses the keyboard.
However currently the prop does not work on Android. I've posted an issue on the subject:
https://github.com/facebook/react-native/issues/8778
Hope it helps other , as wasted time on reading many github threads and issues..
By doing below code , you keyboard show return key you wanted for example "done", "go" and also dismiss the keyboard when you press return key in my case done key while using multi line seamlessly.
import {Textinput, ScrollView, Keyboard} from 'react-native';
// ...code
<TextInput
keyboardType="default"
returnKeyType="done"
multiline={true}
blurOnSubmit={true}
onSubmitEditing={()=>{Keyboard.dismiss()}}
/>
This is a simplified version of austin's very helpful answer above.
If you convert the View you have for your screen/component into a TouchableOpacity with activeOpacity={1} (this prevents any fading or opacity effects), you can then pass it:
onPress={() => Keyboard.dismiss()}
Make sure you've imported TouchableOpacity and Keyboard from 'react-native' and you're good.
Banged my head against this for a few hours and after fiddling around and some dumb luck finally figured out how to make a multiline TextInput dismiss by just touching outside the text box. Little Example for ya'll hope that someone else finds this useful because the docs do not make it all apparent that you can get multilines to dismiss easily...
import React, { Component} from 'react'`
import {
keyboardAvoidingView,
Keyboard,
StatusBar,
StyleSheet,
TextInput,
View,
} from 'react-native';
class App extends Component {
constructor(props){
super(props)
this.state ={
behavior: 'position',
}
this._keyboardDismiss = this._keyboardDismiss.bind(this);
}
componentWillMount() {
this.keyboardDidHideListener =
Keyboard
.addListener('keyboardDidHide',this._keyboardDidHide);
}
componentWillUnmount(){
this.keyboardDidHideListener.remove();
}
_keyboardDidHide() {
Keyboard.dismiss();
}
render() {
return (
<KeyboardAvoidingView
style{{flex:1}}
behavior={this.state.behavior}
>
<TouchableOpacity
onPress={this._keyboardDidHide}
>
<View>
<TextInput
style={
color: '#000',
paddingLeft: 15,
paddingTop: 10,
fontSize: 18,t}
multiline={true}
textStyle={{ fontSize: '20', fontFamily: 'Montserrat-Medium' }}
placeholder="Share your Success..."
value={this.state.text}
underlineColorAndroid="transparent"
returnKeyType={'default'}
/>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
To complete my comment about returnKeyType, you also need to set blurOnSubmit={true} which will trigger onSubmitEditing event handler too when Done is pressed.
See example https://rnplay.org/apps/0HIrmw for details.
as long as there is only one done button, we still need to decide what this return/done button does. In this case I will propose adding another dismiss button to the keyboard by using InputAccessoryView. Following code is copied from https://dev.to/vladimirvovk/react-native-ios-keyboard-with-a-custom-button-15h0
<TextInput
...
inputAccessoryViewID="Next"
/>
<TextInput
...
ref={nextFieldRef}
inputAccessoryViewID="Done"
/>
<InputAccessoryView nativeID="Next">
<View style={styles.accessory}>
<Button
onPress={() => nextFieldRef.current.focus()}
title="Next"
/>
</View>
</InputAccessoryView>
If anyone is still having trouble with this, specifically around keeping the return key for adding new lines and dismissing the keyboard when clicking outside of the input, the regular view component does not blur the TextField when you click outside of the component. However if you use a ScrollView instead of a regular View it will trigger the onBlur effect when you click anywhere on the screen and the below code will dismiss the keyboard and keep the return key for adding new lines. You can set scrollEnabled={false} on the scroll view if you don't want scrolling and it will still trigger the events
<TextField
value={value}
placeholder="Description..."
multiline
numberOfLines={20}
keyboardType="default"
onBlur={() => {
Keyboard.dismiss();
}}
/>
Keyboard.dismiss() solved the issue for me.
<TextInput
keyboardType="default"
returnKeyType="done"
multiline={true}
blurOnSubmit={true}
onSubmitEditing={()=>{Keyboard.dismiss()}}
/>

React Native <ScrollView> persistent scrollbar

After perusing the React Native Documentation I couldn't seem to find out how to make a <ScrollView> have a persistent scrollbar that doesn't fade out. How would I achieve that?
iOS
The underlying iOS native component, UIScrollView (technically, RCTEnhancedScrollView), doesn't support keeping the scroll indicators visible. For this reason, the React Native wrapper around it won't either.
There is a hack to get this working with the native component (see this answer for one approach). To accomplish this in React Native, you'd need to implement this hack on the native side, and then either create your own Native Module or fork React Native and modify their ScrollView component.
That said, the iOS Scroll View interface guidelines discourage this, so you may want to leave the indicators' behavior alone.
Android
A few approaches:
set <item name="android:overScrollMode">always</item>,
set android:fadeScrollbars="false" in XML, or
set ScrollView.setScrollbarFadingEnabled(false) in Java (e.g. in your custom native bridge code)
This is similarly discouraged as nonstandard UI unless you have a strong reason for it.
Adding answer since none of the above worked for me.
Android now has the persistentScrollbar props.
iOS does not support this. So I created a JS solution that can be used as follows:
<SBScrollView persistentScrollbar={true}>...</SBScrollView>
Basically, this functional component will use persistentScrollbar when on Android, while add a bar when we are on iOS. It is not smooth for now, but it is functional.
// #flow
import React, {useState} from 'react';
import {Platform, View, ScrollView} from 'react-native';
type Props = {|
persistentScrollbar?: boolean,
children?: React$Node,
|} & View.propTypes;
export default function SBScrollView({
persistentScrollbar = false,
children,
...other
}: Props) {
const [nativeEvent, setNativeEvent] = useState();
if (Platform.OS === 'android' || !persistentScrollbar) {
// Abdroid supports the persistentScrollbar
return (
<ScrollView persistentScrollbar={persistentScrollbar} {...other}>
{children}
</ScrollView>
);
}
const top = nativeEvent
? nativeEvent.contentOffset.y +
(nativeEvent.contentOffset.y / nativeEvent.contentSize.height) *
nativeEvent.layoutMeasurement.height
: 0;
// iOS does not support persistentScrollbar, so
// lets simulate it with a view.
return (
<ScrollView
scrollEventThrottle={5}
showsVerticalScrollIndicator={false}
onScroll={event => setNativeEvent(event.nativeEvent)}
{...other}>
{children}
<View
style={{
position: 'absolute',
top,
right: 4,
height: 200,
width: 4,
borderRadius: 20,
backgroundColor: 'gray',
}}
/>
</ScrollView>
);
}
I hope this can help others.
I was looking for a solution but I didn't find nothing, then I created a solution, I hope can help you with it.
I created a view View with height and width and put it over my scrollview, after that I used the Props of scrollview like onMomentumScrollBegin, onMomentumScrollEnd, onContentSizeChange and onScroll
after that I make a condition with a boolean variable, if this variable is false, the View is visible, if is false the View is hide, How do I active this variable? with the Prop onMomentumScrollBegin that detect when you use the scrollView and the same way to set the variable in false with onMomentumScrollEnd that detects when the scroll ends.
The Prop onContentSizeChange allows me to get the height and width of my scrollview, this values I used to calculate where would be set the scrollbar/scrollIndicator
and finally with the Prop onScroll I get the position.
the example:
<ScrollView
onMomentumScrollBegin={() => {this.setvarScrollState()}}
onMomentumScrollEnd={() => {this.setvarScrollStateRev()}}
scrollEventThrottle={5}
onContentSizeChange={(w, h) => this.state.hScroll = h}
showsVerticalScrollIndicator={false}
onScroll={event => { this.state.wScroll = event.nativeEvent.contentOffset.y }}
style={{ marginVertical: 15, marginHorizontal:15, width: this.state.measurements3.width}}>
{
Mydata.map((value, index) => {
return <TouchableOpacity>
<Text>{ value.MyDataItem }</Text>
</TouchableOpacity>
})
}
the functions:
setvarScrollState() {
this.setState({VarScroll: true});
}
setvarScrollStateRev() {
this.setState({VarScroll: false});
}
and the variable
this.state = {VarScroll: false}
Then my condition is
!this.state.VarScroll ?
<View
style = {{
marginTop: 200*(this.state.wScroll / this.state.hScroll),
marginLeft:338.5,
height: 35,
width: 2,
backgroundColor: 'grey',
position:'absolute'
}}
/>
: null
Why 200? because is the maximum value that my marginTop can set
Check the picture
Final note:
the scrollView have to be inside a View with the another View (scrollbar)
something like this
<View>
{/*---- ScrollBar and conditions----*/}
<View>
<View>
<ScrollView>
</ScrollView>
</View>