fix(Android): handle keyboard and formsheet together
Description
Fixes https://github.com/software-mansion/react-native-screens/issues/2774, #2664
This issue aims to improve keyboard handling when using formsheet. Currently, we cover the formsheet and move to highest intent (when there are multiple) + force keyboard hide while swiping down. This sometimes look glitchy and causes formsheet to be covered in some scenarios.
Changes
I tested the Jetpack Compose version of form sheet and it didn't really seem to provide consistent experience as it is on iOS, so we modeled the solution to resemble iOS.
Before keyboard and formsheet were independent - one didn't animated with the other. For cases other than fitToContents when keyboard was opened the intent was bumped. When changing intent by swipe gesture, keyboard was dismissed.
New solution consist of three parts:
- Changing intent - this part works as before
- Animation - formsheet is translated based on ime animation progress by
WindowInsetsAnimationCompat.Callback - Close gesture - when keyboard is opened
BottomSheetBehavioronTouchEventis overridden and is used to swipe down the keyboard, SimpleImeAnimationController is used to control keyboard based on https://youtu.be/acC7SR1EXsI?feature=shared&t=718
When keyboard is open on the previous screen, we force trigger onApplyWindowInsets to update screen transitionX based on the keyboard height.
Animating keyboard and controlling it is available from Android API 30, so for backward compatibility we set translateX in onApplyWindowInsets.
Screenshots / GIFs
One intent
| iOS | Android Before | Android After |
|---|---|---|
Two intents
| iOS | Android Before | Android After |
|---|---|---|
Three intents
| iOS | Android Before | Android After |
|---|---|---|
Test code and steps to reproduce
See Test2774.tsx
How this has been tested
- Open formsheet, and check all intents (if available)
- Open form sheet and focus text input - formsheet should be animated with the keyboard by keyboard height or max available space
- Check if keyboard can be closed by gesture
- Check if keyboard can be closed by close button
- Check Android prior API 30
- Check if the formsheet isn't translated outside of the window
- Check if formsheet isn't covered when there is keyboard already opened on the previous screen
Checklist
- [ ] Included code example that can be used to test this change
- [ ] Ensured that CI passes
Hey @maciekstosio
I noticed this work and thought it would be reasonable to create a discussion here. Do you think this functionality can be achieved using KeyboardGestureArea from react-native-keyboard-controller?
I understand the idea behind "a single package can handle navigation/keyboard management", but I also would love to see less DRY in the react-native ecosystem and more composability?..
Would love to know if the result that you are trying to achieve is not possible to achieve with KeyboardGestureArea and if not then maybe I can help to implement it in react-native-keyboard-controller? 👀
This looks amazing!
Keyboard handling is the last issues that I have on new arch to make it work flawlessly. 🤞
Hey guys, is there any plans to merge it soon?
Hi, when are you planning to merge? This is a merge that many people are really waiting for.
Up! up!
Ping @kkafar
The work progresses here: https://github.com/software-mansion/react-native-screens/pull/3248