Hide keyboard in react-native

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()

https://github.com/facebook/react-native/pull/9925

use this for custom dismissal

var dismissKeyboard = require('dismissKeyboard');

var TestView = React.createClass({
    render: function(){
        return (
            <TouchableWithoutFeedback 
                onPress={dismissKeyboard}>
                <View />
            </TouchableWithoutFeedback>
        )
    }
})

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.

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.

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.

You can import keyboard from react-native like below:

import { Keyboard } from 'react-native';

and in your code do something like this:

render() {
    return (
      <TextInput
        onSubmit={Keyboard.dismiss}
      />
    );
  }

static dismiss()

Dismisses the active keyboard and removes focus.

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>

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()

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.

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.

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>
        )
    }
}

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.

in ScrollView use

keyboardShouldPersistTaps="handled" 

This will do your job.

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 unhandledTouches (and then dismiss the keyboard):

```

 handleUnhandledTouches(){
   Keyboard.dismiss
   return false;
 }

 render(){
    <View style={{ flex: 1 }} onStartShouldSetResponder={this.handleUnhandledTouches}>
       <MyApp>
    </View>
  }

```

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.

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

flutter websocket connection issue