Adjust Android Keyboard - AndreiRadchenko/scenery GitHub Wiki

Adjust android keyboard on Login and Registration form.

Solution on expo issues | Stackowerflow discuss

I want to achieve form that move up on keyboard show. Keyboard obscures bottom part of form. Background image hasn't move.

I didn't use KeyboardAvoidingView, instead I did it by:

  • prevent any keyboard impact on view. It's native behavior for iOs but not trivial for android.
  • Next step was to create animation for form translate on keyboardWillShow (ios) and keyboardDidShow (android doesn't have keyboardWillShow)

First of all you need to understand three modes for softwareKeyboardLayoutMode: pan, resize in expo app.json (adjustPan, adjustResize, adjustNothing in android activity manifest .xml)

How to prevent android view resizing or moving on keyboard show in expo

Expo doesn't have adjustNothing mode. To achieve such behavior for android app I left default resize mode for softwareKeyboardLayoutMode in expo app.json and used trick in component composition: style={{ minHeight: Math.round(Dimensions.get('window').height) + StatusBar.currentHeight

You should probably add StatusBar.currentHeight if background image cover status bar as well.

Pay attention to style={{flex: 1}} in outer components.

import { Dimensions, StatusBar } from 'react-native';

export const LoginScreen = ({ navigation, route }) => {

  const translateAnim = useFormAnimation({
    formOffset: isPlatformIOS ? 250 : screenHeight * 0.36,
    animationDuration: Platform.OS === 'ios' ? 200 : 200,
  });

    return (
    <TouchableWithoutFeedback
      onPress={() => {
        Keyboard.dismiss();
      }}
    >
      <Styled.Container>
        <Styled.BgImage
          resizeMode="cover"
          source={require('../../../../assets/img/PhotoBG-compressed.jpg')}
          style={{ minHeight: Math.round(Dimensions.get('window').height) + StatusBar.currentHeight}} // <-- This trick doesn't allow BgImage resize on keyboard show
        >
          <Styled.LoginForm
            keyboardHeight={keyboardHeight}
            isPlatformIOS={isPlatformIOS}
            style={{
              transform: [
                { scale: 1 },
                { rotateY: '0deg' },
                { perspective: 100 },
                { translateY: translateAnim },
              ],
            }}
          >

Form translate animation on keyboard show

useFormAnimation.js

import { Animated, Platform } from 'react-native';
import { useState, useEffect } from 'react';
import { useKeyboardVisible } from './useKeyboardVisible';

export const useFormAnimation = ({ formOffset, animationDuration }) => {
  const keyboardHeight = useKeyboardVisible();
  const [translateAnim] = useState(new Animated.Value(+0));
  const [translateTo, setTranslateTo] = useState(+0);

  useEffect(() => {
    Animated.timing(translateAnim, {
      toValue: translateTo,
      duration: animationDuration,
      useNativeDriver: true,
    }).start();
  }, [translateTo]);

  useEffect(() => {
    keyboardHeight
      ? setTranslateTo(
          Platform.OS === 'ios'
            ? formOffset - keyboardHeight
            : formOffset - keyboardHeight
        )
      : setTranslateTo(+0);
  }, [keyboardHeight]);

  return translateAnim;
};

useKeyboardVisible.js

import { useState, useEffect, useCallback } from 'react';
import { Keyboard, Platform } from 'react-native';

export const useKeyboardVisible = () => {
  const [keyboardHeight, setKeyboardHeight] = useState(0);

  useEffect(() => {
    const keyboardDidShowListener = Keyboard.addListener(
      'keyboardDidShow',
      (event) => {
        setKeyboardHeight(event.endCoordinates.height);
      }
    );
    const keyboardDidHideListener = Keyboard.addListener(
      'keyboardDidHide',
      () => {
        setKeyboardHeight(0);
      }
    );

    return () => {
      keyboardDidHideListener.remove();
      keyboardDidShowListener.remove();
    };
  }, []);

  return keyboardHeight;
};

Links for future research:

Create native modules to change adjustResize/adjustPan in screen | react-native-keyboard-controller

Consider that using custom library deprives you of testing app in Expo Go

View behavior with keyboard open on Comments screen.

With softwareKeyboardLayoutMode: 'resize' keyboard can decrease height of screen components if they have height set in percent or if its component is descendant of ScrollView. In first case root view height will be recalculated (initial screen height minus keyboard height) and component will occupy it percent part of recalculated root view. In second case ScrollView will shrink to make room for keyboard.

Screen comments.js

import { FlatList, KeyboardAvoidingView, Platform } from 'react-native';

import { CommentCard } from '../../../../components/CommentCard';
import { InputBottomBar } from '../../../../components/InputBottomBar';

import * as Styled from './Comments.styled';
import authors from '../../../../mock/authors.json';

const isPlatformIOS = Platform.OS === 'ios';

const ImageCard = ({ url }) => {
  return <Styled.ImageCard source={{ uri: url }} />;
};

export const CommentsScreen = ({ navigation, route }) => {
  const { image, comments } = route.params.post;

  return (
    <KeyboardAvoidingView
      style={{ flex: 1 }}
      behavior={isPlatformIOS ? 'padding' : null}
      keyboardVerticalOffset={isPlatformIOS ? 65 : 0}
      keyboardShouldPersistTaps="handled"
    >
      <Styled.CommentsContainer>
        <FlatList
          data={comments}
          renderItem={({ item, index }) => {
            const avatar = authors.find((e) => e._id === item.authorId);
            const isLastComment = index === comments.length - 1;
            return (
              <CommentCard
                {...item}
                index={index}
                avatar={avatar}
                isLastComment={isLastComment}
              />
            );
          }}
          ListHeaderComponent={<ImageCard url={image.url} />}
        />
        <InputBottomBar />
      </Styled.CommentsContainer>
    </KeyboardAvoidingView>
  );
};