35

I have a login screen created using react-native.

How can I shift my screen up when the user is typing in the textInput?

Do I listen to the onFocus() event and use css styling to change the style of the view?

Mahdi Bashirpour
  • 17,147
  • 12
  • 117
  • 144
Steve Ng
  • 1,189
  • 1
  • 13
  • 35
  • We need more context/code here. – José Ricardo Pla May 30 '15 at 13:30
  • possible duplicate of [How to auto-slide the window out from behind keyboard when TextInput has focus?](http://stackoverflow.com/questions/29313244/how-to-auto-slide-the-window-out-from-behind-keyboard-when-textinput-has-focus) – Sherlock Sep 15 '15 at 19:17
  • You may want to theck [this](https://github.com/facebook/react-native/issues/3195#issuecomment-146568644) Github issue. We're discussing how to achieve this in there. – amb Oct 13 '15 at 07:28

7 Answers7

30

In 2017 (RN 0.43) there is special component for this: KeyboardAvoidingView

Priyam Mohanty
  • 461
  • 4
  • 15
  • 5
    @farmcommand2 it works, but with some hacks. There is [issue](https://github.com/facebook/react-native/issues/11681#issuecomment-339446150) on GitHub with solutions. I use this one: `behavior={(Platform.OS === 'ios') ? 'padding' : null}` – Aleksandr Mansurov Nov 21 '17 at 20:09
  • Kind of crappy. I'd rather implement the keyboard event by myself. so you really do have control over the behaviour of your Layout – Louis Lecocq Feb 15 '18 at 18:06
  • As of December 2018, it seems to be working in Android as well. – Sumit Dec 10 '18 at 17:41
  • 9
    Unfortunately KeyboardAvoidingView is a pure garbage and does not really work. – Petr Peller Mar 27 '20 at 16:48
8

You can use ScrollView to control screen up and down movements. As long as user hasn't focused any TextInput, you can disable scroll. On focus, just shift up the scrollview using Content Offset prop.

<TextInput
    onFocus={this.textInputFocused.bind(this)}
  />

textInputFocused() {
//do your stuff here. scroll screen up
}

Hope it helps!

NightFury
  • 13,436
  • 6
  • 71
  • 120
7
import {KeyboardAvoidingView} from 'react-native';

<KeyboardAvoidingView style={styles.container} behavior="padding" enabled>

    <Text style={{height: 100, marginTop: 30}}> test text before input</Text>
    <Text style={{height: 100, marginTop: 30}}> test text before input</Text>
    <Text style={{height: 100, marginTop: 30}}> test text before input</Text>
    <Text style={{height: 100, marginTop: 30}}> test text before input</Text>
    <Text style={{height: 100, marginTop: 30}}> test text before input</Text>

    <TextInput
        style={{height: 40, borderColor: 'gray', borderWidth: 1}}
        onChangeText={(text) => this.setState({text})}
        value={this.state.text}
    />

    <Text style={{height: 100, marginTop: 20}}>1 test text after input</Text>
    <Text style={{height: 100, marginTop: 20}}>2 test text after input</Text>
    <Text style={{height: 100, marginTop: 20}}>3 test text after input</Text>
    <Text style={{height: 100, marginTop: 20}}>4 test text after input</Text>
    <Text style={{height: 100, marginTop: 20}}>5 test text after input</Text>

</KeyboardAvoidingView>

Run in snack : https://snack.expo.io/H1BE5ZoXV

Mahdi Bashirpour
  • 17,147
  • 12
  • 117
  • 144
6

Night Fury's answer is pretty good, though wouldn't fuss with the ScrollView's contentOffset, I'd use the ScrollResponder:

render() {
  return (
    <ScrollView ref="myScrollView">
      <TextInput
        ref="myInput"
        onFocus={this._scrollToInput.bind(this)}
      />
    </ScrollView>
  );
}

_scrollToInput {
  const scrollResponder = this.refs.myScrollView.getScrollResponder();
  const inputHandle = React.findNodeHandle(this.refs.myInput)

  scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
    inputHandle, // The TextInput node handle
    0, // The scroll view's bottom "contentInset" (default 0)
    true // Prevent negative scrolling
  );
}

See the method definition: scrollResponderScrollNativeHandleToKeyboard

Antoine
  • 5,504
  • 5
  • 33
  • 54
  • This will bust on getScrollResponder I'm guessing this is worked on an older version of the api. I use scrollTo and hard code the numbers its a horrible solution and I'm half tempted to just use webviews for form pages. – aintnorest Jul 15 '15 at 02:06
  • It works fine to move the screen up, what should I use to move screen down again after finishes typing? – Georgi Kovachev Dec 02 '15 at 07:25
2

This package does a greate job, introduces a KeyboardAwareScrollView component that scrolls the view up matching the input with the keyboard, then scrolling back down.

Amr Draz
  • 2,806
  • 3
  • 20
  • 26
0

And another solution, working with RN 0.2, this time instead of squashing the content it scrolls.

 inputFocused: function(ref) {
   this._scroll(ref, 75);
 },

 inputBlurred: function(ref) {
   this._scroll(ref, 0);
 },

 _scroll: function(ref, offset) {
   setTimeout(() => {
     var scrollResponder = this.refs.myScrollView.getScrollResponder();
     scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
                React.findNodeHandle(this.refs[ref]),
                offset,
                true
            );
     });
  },

...

render: function() {
  return <View style={{flex: 1}}> 
    <ScrollView ref="myScrollView" keyboardDismissMode='interactive' contentContainerStyle={{flex: 1}}>
      <TextInput
        ref="myInput"
        onFocus={this.inputFocused.bind(this, 'myInput')}
        onBlur={this.inputBlurred.bind(this, 'myInput')} />
    </ScrollView>
  </View>
}
pomo
  • 2,251
  • 1
  • 21
  • 34
0

It's a crap shoot to get the native keyboard awareness functionality of ScrollView working. For my Android app, it works perfectly in one screen that is nearly identical as the other for which doesn't work. And on iOS, it just doesn't work. This is what's working for me:

import { Keyboard, ScrollView, StyleSheet, View } from 'react-native';

this.state = {
    filler: false,
}

componentWillMount() {
    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
    this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide.bind(this));
}

componentWillUnmount() {
    this.keyboardDidShowListener.remove();
    this.keyboardDidHideListener.remove();
}

_keyboardDidShow() {
    this.setState({filler: true})
    setTimeout(() => this.vertical && this.vertical.scrollToEnd({animated: true}), 0);
}

_keyboardDidHide() {
    this.setState({filler: false})
}


...
return (
  <ScrollView ref={ref => this.vertical = ref}>
    <TextInput/>
    { this.state.filler ? <View style={styles.filler}/> : null }
  </ScrollView>
)

styles.filler = {
    height: 'Keyboard Height'
}

Note: This might only work if your <TextInput/> is at the bottom of the screen which it was in my case.

Friendly-Robot
  • 1,124
  • 14
  • 24
  • 1
    use arrow function from es6 to avoid the bind method https://stackoverflow.com/questions/32192682/react-js-es6-avoid-binding-this-to-every-method And you can also improve it by implementing `keyboardWillShow' on iOS. (isn't implemented on Android) – Louis Lecocq Feb 15 '18 at 18:03
  • Excellent suggestions. I was noticing that the response frame of the TextInput appearing above the keyboard was a bit behind. Thanks! – Friendly-Robot Feb 16 '18 at 19:49