ifelsething logoifelsething

What is Keyboard.scheduleLayoutAnimation() Method?

Published On: 2024-02-28
Posted By: Harish

What is Keyboard.scheduleLayoutAnimation() Method?

Sometimes we need simple animations for views in our app. We can use React Native Animation api for animating a view or a component.

But if you are already using the Keyboard module , then you can use the Keyboard.scheduleLayoutAnimation() method to animate views. This method is just a wrapper with LayoutAnimation.configureNext method implemention.

To understand this, we will create a simple search text input animation which expands with the keyboard.

Create A New Project

Create a new react-native project by using npx. Check documentation for creating a new react native project.

npx react-native@latest init KeyboardRN

Import Required Components and Modules

We need to import a Keyboard module, a TextInput for search and Pressable to wrap the text input for closing the keyboard when clicked outside as we discussed in our post.

//app.tsx
import {
  View,
  StyleSheet,
  TextInput,
  Pressable,
  Keyboard
} from "react-native";

...
<Pressable style={styles.container}>
  <TextInput
    style={styles.input}
    keyboardType='default'
    returnKeyType='search'
    placeholder="Search"
    placeholderTextColor='gray'
  />
</Pressable>
...

Now, we want to close the opened keyboard when clicked outside of the text input. So, we use the Keyboard.dismiss() method to dismiss the keyboard inside the onPress callback of the Pressable component.

Our example implementation is to expand the search box when the keyboard is opened and to shrink when the keyboard is closed. So, we will add basic input style and another style which conditionally applies to text input based on the keyboard state.

To get the state of the keyboard, we will create a state variable isKeyboardOpen using the useState hook. This is a boolean which stores true or false based on the keyboard state.

Use this state to conditionally apply or remove style to animate search input.

...

const [isKeyboardOpen, setIsKeyboardOpen] = useState<boolean>(false);

...

<Pressable
  onPress={() => {
    Keyboard.dismiss();
  }}
>
<TextInput
  style={[
    styles.input,
    isKeyboardOpen && styles.after_input
  ]}
  ...
/>
</Pressable>

...

We need to know when the keyboard is opening or closing to animate our text input. We already know that the keyboard will close when tapped outside because of Pressable onPress callback. And with text input's onSubmitEditing callback, we will get to know when the keyboard's search/return button is tapped. So, set the state to false in both Pressable's onPress callback and TextInput's onSubmitEditing callback.

Now, by using text input's onFocus callback, we will know when text input gets focused which results in showing the keyboard. So add onFocus callback and set isKeyboardOpen value to true.

...

<Pressable
  onPress={() => {
    setIsKeyboardOpen(false);
    Keyboard.dismiss();
  }}
>
<TextInput
  ...
  ...
  onFocus={() => {
    setIsKeyboardOpen(true);
  }}
  onSubmitEditing={() => {
    setIsKeyboardOpen(false);
  }}
/>
</Pressable>

...

If we run the app,

#for Android
npx react-native run-android

#for ios
npx react-native run-ios

We will get results similar like below gif,

Keyboard.scheduleLayoutAnimation() example before

As you can see that search input gets expanded or shrunken immediately without any animation.

Now to animate that, we will use the Keyboard.scheduleLayoutAnimation() method. As we know that this method is a wrapper of react native animation api's createNext method, which implements on next re-render. In our case, re-render happens because we are updating the state value to true or false on opening or closing of the keyboard.

Keyboard.scheduleLayoutAnimation() takes the KeyboardEvent type. And in that, we need to pass duration and easing as main values, other onEndCoordinates can be ignored.

Create a scheduleLayoutAnimation named method with Keyboard.scheduleLayoutAnimation() implementation and add this method at each place where we are updating the keyboard state value.

import {
  ...
  Keyboard,
  KeyboardEventEasing
} from "react-native";

type KeyboardAnimationEvent = {
  duration: number,
  easing: KeyboardEventEasing
};

...

const scheduleLayoutAnimation = (duration: number) => {
  const anim: KeyboardAnimationEvent = {
    duration: duration,
    easing: 'linear'
  };
  Keyboard.scheduleLayoutAnimation(anim);
};
  ...
<Pressable
  onPress={() => {
    scheduleLayoutAnimation(250);
    setIsKeyboardOpen(false);
    Keyboard.dismiss();
  }}
>
<TextInput
  ...
  ...
  onFocus={() => {
    scheduleLayoutAnimation(250);
    setIsKeyboardOpen(true);
  }}
  onSubmitEditing={() => {
    scheduleLayoutAnimation(250);
    setIsKeyboardOpen(false);
  }}
/>
</Pressable>

...

As animation happens after a re-render, make sure that animation schedule method is implemented before updating the state value.

If we run the app now, we get the below result.

Keyboard.scheduleLayoutAnimation() example after

Animation works perfectly for iOS devices, but for android devices we have to enable experimental layout animation property to work like below,

import {
  ...
  Platform,
  UIManager
} from "react-native";

if (
  Platform.OS === 'android' &&
  UIManager.setLayoutAnimationEnabledExperimental
) {
  UIManager.setLayoutAnimationEnabledExperimental(true);
}

...

export const App = () => {

...

Please note that Keyboard's scheduleLayoutAnimation method only accepts KeyboardEventEasing type which does not have spring animation type and other props. But this method is a quick way to add an animation if the Keyboard module is used.

We can also use keyboardWillShow and keyboardWillHide events to get the state before the keyboard event. But these only work for apple devices not for android devices. So I haven’t used that implementation in this post. If you are only focusing on iOS devices, you can go with listener event methods too.

Similar implementation with LayoutAnimation module can be seen in below complete code.

import { useState } from "react";
import {
  View,
  Text,
  StyleSheet,
  TextInput,
  Keyboard,
  Platform,
  LayoutAnimation,
  UIManager,
  Pressable,
  KeyboardEventEasing
} from "react-native";

type KeyboardAnimationEvent = {
  duration: number,
  easing: KeyboardEventEasing
};

if (
  Platform.OS === 'android' &&
  UIManager.setLayoutAnimationEnabledExperimental
) {
  UIManager.setLayoutAnimationEnabledExperimental(true);
}

export const KeyboardAnimation = () => {
  const [isKeyboardOpen, setIsKeyboardOpen] = useState<boolean>(false);
  const scheduleLayoutAnimation = (duration: number) => {
    const anim: KeyboardAnimationEvent = {
      duration: duration,
      easing: 'linear'
    };
    Keyboard.scheduleLayoutAnimation(anim);
  };

  // This method is a similar implementation like Keyboard.scheduleLayoutAnimation method
  // using LayoutAnimation module 
  const animationUsingLayoutAnimation = (duration: number) => {
    LayoutAnimation.configureNext({
      duration: duration,
      update: { type: 'linear' },
    });
  };

  return (
    <View style={styles.container}>
      <Pressable
        style={styles.container}
        onPress={() => {
          scheduleLayoutAnimation(250);
          //animationUsingLayoutAnimation(250);
          setIsKeyboardOpen(false);
          Keyboard.dismiss();
        }}>
        <Text style={styles.text}>
          ifelsething.com
        </Text>
        <Text style={styles.text}>
          keyboard.scheduleLayoutAnimation() example
        </Text>
        <TextInput
          style={[
            styles.input,
            isKeyboardOpen && styles.after_input
          ]}
          keyboardType='default'
          returnKeyType='search'
          placeholder="Search"
          placeholderTextColor='gray'
          onFocus={() => {
            scheduleLayoutAnimation(250);
            //animationUsingLayoutAnimation(250);
            setIsKeyboardOpen(true);
          }}
          onSubmitEditing={() => {
            //animationUsingLayoutAnimation(250);
            scheduleLayoutAnimation(250);
            setIsKeyboardOpen(false);
          }}
        />
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    margin: 10,
    gap: 20,
  },
  input: {
    borderColor: 'blue',
    borderWidth: 1,
    borderRadius: 10,
    padding: 10,
    fontSize: 15,
    color: 'black',
    width: 200,
    alignSelf: 'center',
  },
  after_input: {
    width: '100%'
  },
  text: {
    fontSize: 15,
    color: 'black',
    fontStyle: 'italic'
  },
});

Share is Caring

Related Posts