The hidden Text Input of React Native apps

This my first take on looking for a straightforward unified solution to responding to Keyboard open–change–hide events in React Native. There are great solutions that work great in some specific cases. But I’ve tried and failed to get them work great together.

This is the first article to cover this broad topic to invite others to share their experience. I am sharing mine together with some code samples. I am looking for your comments and thoughts.

The new “average”

Look at the list of the most used apps in the app stores. Most people have had experience with most of them. Those apps provide some of the best, if not the top user experience on mobile. That’s what they, I mean we all expect the same from your app.

Specifically, we expect apps to:

  1. keep the content accessible while the keyboard is open;
  2. dismiss the keyboard interactively while scrolling down the the bottom edge of the screen;
  3. auto-scroll to the next input;
  4. safely avoid the front facing camera notches and other indicators;
  5. move floating text input above the keyboard when changing languages;
  6. and of course the buttery smooth animations.

We all want that to work seamlessly and all together.

And we want that in our apps too. People using our apps expect the same high–class experience as with apps with way more budget to have it.

Anything else is…well… less than average. But it all looks more like this…

Our solution needs to:

#1: Avoid the Keyboard

Solutions:

  1. import { KeyboardAvoidingView } from 'react-native' by the Core Team;
  2. import KeybardSpacer from 'react-native-keyboard-spacer' by Andrew Hurst;

To make it a bit more complicated, on Android you can set the android:windowSoftInputMode attribute to tell your your app (activity) how it should respond to Keyboard popping-out in the App’s manifest file.

Making iOS and Android play well together gets complicated if you want to…

#2: Avoid the device notches

Solutions:

  1. import { SafeAreaView } from 'react-native' by the Core Team;
  2. import SafeAreaView from 'react-native-safe-area-view' by the React Native Community.

The Community version is just rolling new version that could support also the growing number of Android devices with obscured notches, home buttons by implementing React Native Safe Area Context library.

There are some (hard to replicate) edge cases where the SafeAreaView fails to restore the safe padding or, on the other hand fails to remove padding after it stops touching the edge.

Implementation of the above two behaves differently. The community version works mostly more reliable.

Problem #3: Using with ScrollView, solutions:

  1. import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view' from APSL to automatically adjust ScrollView and scroll to recently focused TextInput when the Keyboard opens.
    ~ How to implement with SafeAreaView (and which) is questionable.
  2. You try to implement it yourself using import { SafeAreaView, KeyboardAvoidingView, ScrollView } from 'react-native' .

Solution(s)
TL;DR: Below solutions might work for some cases–at best 80/20 solutions.

First version: Naive

Wish this would work out-of-the-box.

A fairly simple and naive Screen container component implementation with using only the core React Native components. Not suitable for production, just for educational purposes.

Pros:

  • initial scroll-to the focused field for free; Keyboard size changes do not handle TextInput being obscured by the keyboard;
  • uses just the core components;
  • simple with minimum boilerplate;
  • quite similar behavior with Android with android:windowSofInputMode="adjustResize" set in the manifest;

Cons:

  • fails to scroll to focused input correctly with React Navigation’s header present;
  • core SafeAreaView in ScrollView “collapses” after the Keyboard closes;
  • padding of the KeyboardAvoidingView obscures the view when ScrollView’s keyboardDismissMode is set to interactive.

But we can do better than…

Version 2: Keep Safe Area View active

Just by swapping the core SafeAreaView with the Community version, it won’t collapse after closing the keyboard.

Cons:

  • Safe area stays active when keyboard is open and keeps extra padding all the time.

The keyboard interactive dismiss mode still fails to work… but this is kind of… better. (I guess.)

Version 3: Automatic scroll to with Header

If the distance between the top of the screen and the react native view is non-zero (e.g. Header from React Navigation), the Screen container fails to scroll to the input correctly.

If you don’t need the header, the failing scroll to focused input can be overcome by navigationOptions: { header: null } on screen component. That removes the header crated by React Navigation.

But in most cases, you need to keep the header.

Let’s measure the position of the container and pass the keyboardVerticalOffset to the KeyboardAvoidingView.

There are still few things we might want to improve, but in most cases this solution should work quite decently.

To be continued…

What still needs to be solved:

  • Interoperability with Android;
  • Disable SafeAreaView when Keyboard is open, reactive on close;
  • Scroll-to the next focused input;
  • Adapt to Keyboard size change;
  • Dismiss keyboard on fast scroll, keep open on slow scroll;
  • Interactive keyboard dismissal on iOS (mostly a nice to have);
  • … something else I haven’t thought of.

Leave your comment below. I’m looking forward to what you find out.

One that loves design, illustration, photography, digs in code, adores his dog and enjoys life & good coffee. http://be.net/martin_adamko