Problems with keyboardAvoidView

I’m having trouble with my login view on Android. Whenever I click on an input field, the keyboard show up and push the view up too much. On iOS, it works flawlessly. I tried the KeyboardAvoidView and the react-native-keyboard-aware-scroll-view lib. Nothing works.

I put my KeyboardAvoidView in different places, it didn’t solve my problem. Im running out of options. As far as I know, Android does it automatically, but what can I do when it doesn’t do it right?

Here is my code:

import React, { Component } from 'react';
import { 
  View, 
  Text, 
  StyleSheet, 
  TextInput, 
  TouchableOpacity, 
  Alert, 
  Linking, 
  ImageBackground, 
  KeyboardAvoidingView, 
  Platform
} from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Constants } from 'expo';

import { login } from '../redux/actions';

import Button from '../components/Button';
import CustomCheckBox from '../components/CustomCheckBox';
import ErrorBox from '../components/ErrorBox';

const recuperarSenhaUrl = 'https://baladapp.com.br/recuperar-senha';
const backgroundImage = require('../../assets/images/splash.png')

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  keyboardViewContainer: {
    width: '100%', 
    alignItems: 'center'
  },
  input: {
    width: '80%',
    height: 16.7 * 3,
    borderRadius: 1.7 * 3,
    fontSize: 4.7 * 3,
    fontFamily: 'roboto-medium-500',
    backgroundColor: '#ffffff',
    paddingHorizontal: 6 * 3,
  },
  esqueceuView: {
    width: '80%',
  },
  esqueceuSenha: {
    fontFamily: 'roboto-medium-500',
    letterSpacing: 0,
    color: '#ffffff',
    fontSize: 5 * 3,
    marginTop: 8 * 3,
    marginBottom: 8 * 3,
  },
  buttonText: {
    fontFamily: 'roboto-medium-500',
    color: '#ffffff',
    fontSize: 4.7 * 3,
  },
  button: {
    borderRadius: 1.7 * 3,
    backgroundColor: '#de0059',
  },
  continuarConectadoView: {
    flexDirection: 'row',
    width: '80%'
    // justifyContent: 'space-between'
  },
  continuarConectadoText: {
    fontFamily: 'roboto-medium-500',
    letterSpacing: 0,
    color: '#ffffff',
    fontSize: 5 * 3,
    marginTop: 2 * 3,
    marginBottom: 8 * 3,
    marginLeft: 3 * 3
  },
  versao: {
    color: '#ffffff',
    fontFamily: 'roboto-regular',
    fontSize: 16,
    position: 'absolute',
    top: '90%'
  }
});

class Login extends Component {
  constructor(props) {
    super(props);

    this.fazerLogin = this.fazerLogin.bind(this);
    this.switch = this.switch.bind(this);
    this.state = {
      email: '',
      senha: '',
      continuarConectado: true
    };
  }

  esqueciMinhaSenha = () => {
    // this.props.navigation.navigate('RecuperarSenha', {email: this.state.email})
    Linking.openURL(recuperarSenhaUrl)
      .catch(err => {
        Alert.alert('Ops, um erro ocorreu', err, [{ text: 'OK' }], { cancelable: false });
      }
    );
  };

  fazerLogin() {
    const { email, senha, continuarConectado } = this.state;
    this.props.login(email.trim(), senha.trim(), continuarConectado);
  }
  
  switch(value) {
    this.setState({continuarConectado: value});
  }

  render() {
    const { erroLogin, logando } = this.props;

    return (
      <ImageBackground style={styles.container} source={backgroundImage}>
        <KeyboardAvoidingView 
          style={styles.keyboardViewContainer} 
          behavior={Platform.OS === 'ios' ? 'padding' : null}
        >
          <Text
            style={{
              fontFamily: 'roboto-bold',
              color: '#ffffff',
              fontSize: 48,
              marginBottom: 20.7 * 3,
            }}
          >
          Balad<Text style={{ fontFamily: 'roboto-light', color: '#ffffff', fontSize: 48 }}>APP</Text>
          </Text>

          <TextInput
            value={this.state.email}
            placeholder="Usuário"
            style={[styles.input, { marginBottom: 4 * 3 }]}
            placeholderTextColor="#828282"
            maxLength={255}
            autoCorrect={false}
            keyboardType="email-address"
            autoCapitalize="none"
            returnKeyType="done"
            underlineColorAndroid="transparent"
            onChangeText={text => this.setState({ email: text })}
          />

          <TextInput
            value={this.state.senha}
            placeholder="Senha"
            style={styles.input}
            placeholderTextColor="#828282"
            maxLength={255}
            autoCorrect={false}
            autoCapitalize="none"
            returnKeyType="done"
            secureTextEntry
            underlineColorAndroid="transparent"
            onChangeText={text => this.setState({ senha: text })}
          />

          <View style={styles.esqueceuView}>
            <TouchableOpacity onPress={this.esqueciMinhaSenha}>
              <Text style={styles.esqueceuSenha}>Esqueceu a senha?</Text>
            </TouchableOpacity>
          </View>

          <CustomCheckBox style={styles.continuarConectadoView} onValueChange={this.switch} value={this.state.continuarConectado}>
            <Text style={styles.continuarConectadoText}>Manter conectado</Text>
          </CustomCheckBox>

          <View style={{ height: 20 * 3, width: '80%' }}>
            <Button
              title="ACESSAR SISTEMA"
              onPress={() => this.fazerLogin()}
              titleStyle={styles.buttonText}
              buttonStyle={styles.button}
              loading={logando}
            />
          </View>
        </KeyboardAvoidingView>

        {erroLogin && (
          <View style={{ width: '80%', height: '10%', borderRadius: 1.7 * 3, marginTop: '5%' }}>
            <ErrorBox
              defaultMessage={
                erroLogin.response.status === 401
                  ? 'Email ou senha incorretos'
                  : 'Ops, houve um erro. Tente novamente'
              }
            />
          </View>
        )}

        <Text style={styles.versao}>{Constants.manifest.version}v</Text>
      </ImageBackground>
    );
  }
}

function mapDispatchToProps(dispatch) {
  return {
    login: (email, senha, continuarConectado) => dispatch(login(email, senha, continuarConectado)),
  };
}

function mapStateToProps(state) {
  return {
    erroLogin: state.config.erroLogin,
    logando: state.config.logando,
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Login);

/* eslint-disable */
Login.propTypes = {
  erroLogin: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
  logando: PropTypes.bool.isRequired,
  login: PropTypes.func.isRequired
}

Here what happens (it cuts the title “Baladapp” in half):

Hey there @otaviogaiao - sorry this is giving you trouble! I can’t run the code you provided since it depends on some other external components. However, have you played around with different behavior options in the KeyboardAvoidingView? Looks like you have it set to null for Android currently. If none of those work, what you may need to do is wrap everything inside of your KeyboardAvoidingView inside of a ScrollView.

Let us know if that still isn’t working for you.

1 Like

Wrapping everything in a ScrollView solved the problem! Thank you so much, I was about to eject to try to fix the problem on the native side!

Could you please explain to me why do I have to wrap in a ScrollView, since the docs don’t say anything about it. (Just want to learn a little more)

1 Like

Glad that worked! :slight_smile: KeyboardAvoidingView, under the hood, is really just a normal RN View with a couple of extra hooks to respond to keyboard events. Normal Views are not scrollable at all, so it was just trying to squish everything into a smaller space, which resulted in the screenshot you posted.

Anytime you want a view to be scrollable in RN, you have to be explicit about it by using a ScrollView - unlike on the web, things do not automatically become scrollable when the container overflows.

Hope that makes sense!

3 Likes

It does! Thank you very much!

1 Like

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.