Skip to content

Commit 54acad4

Browse files
authored
fix(Android): fix formSheet not adjusting for keyboard when it contains input with autofocus (#2911)
## Description Sometimes, formSheet that contains input with autoFocus would not account for the keyboard and would remain behind it. This is because `SheetDelegate` is set as a `OnApplyWindowInsetsListener` when fragment is resumed - this happens after finishing formSheet entering animations (animators impact fragment lifecycle). Sometimes, IME insets were applied before this handler was set. Now, we can use `WindowInsetsAnimationCallback` added in [this PR](#2909). Its `onPrepare` method is called before `onApplyWindowInsets` therefore making it possible to set `OnApplyWindowInsetsListener` in time. ## Changes - change `insetsObserverProxy`'s `listeners` to `HashSet` (we might try to add the same listener multiple times) - override `onPrepare` method in `WindowInsetsAnimationCallback` - change visibility of `SheetDelegate` to `internal` ## Test code and steps to reproduce Open `Test2895` screen in the example app, open & close the formSheet (multiple times). ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
1 parent 3c3db41 commit 54acad4

File tree

5 files changed

+25
-11
lines changed

5 files changed

+25
-11
lines changed

android/src/main/java/com/swmansion/rnscreens/InsetsObserverProxy.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import com.facebook.react.bridge.ReactApplicationContext
1010
import java.lang.ref.WeakReference
1111

1212
object InsetsObserverProxy : OnApplyWindowInsetsListener, LifecycleEventListener {
13-
private val listeners: ArrayList<OnApplyWindowInsetsListener> = arrayListOf()
13+
private val listeners: HashSet<OnApplyWindowInsetsListener> = hashSetOf()
1414
private var eventSourceView: WeakReference<View> = WeakReference(null)
1515

1616
// Please note semantics of this property. This is not `isRegistered`, because somebody, could unregister

android/src/main/java/com/swmansion/rnscreens/Screen.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.swmansion.rnscreens.bottomsheet.useSingleDetent
3131
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
3232
import com.swmansion.rnscreens.events.HeaderHeightChangeEvent
3333
import com.swmansion.rnscreens.events.SheetDetentChangedEvent
34+
import com.swmansion.rnscreens.ext.asScreenStackFragment
3435
import com.swmansion.rnscreens.ext.parentAsViewGroup
3536

3637
@SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated.
@@ -541,6 +542,22 @@ class Screen(
541542
}
542543
}
543544

545+
override fun onAttachedToWindow() {
546+
super.onAttachedToWindow()
547+
548+
// Insets handler for formSheet is added onResume but it is often too late if we use input
549+
// with autofocus - onResume is called after finishing animator animation.
550+
// onAttachedToWindow is called before onApplyWindowInsets so we use it to set the handler
551+
// earlier. More details: https://github.com/software-mansion/react-native-screens/pull/2911
552+
if (usesFormSheetPresentation()) {
553+
fragment?.asScreenStackFragment()?.sheetDelegate?.let {
554+
InsetsObserverProxy.addOnApplyWindowInsetsListener(
555+
it,
556+
)
557+
}
558+
}
559+
}
560+
544561
private fun dispatchSheetDetentChanged(
545562
detentIndex: Int,
546563
isStable: Boolean,

android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class ScreenStackFragment :
7474

7575
private var dimmingDelegate: DimmingViewManager? = null
7676

77-
private var sheetDelegate: SheetDelegate? = null
77+
internal var sheetDelegate: SheetDelegate? = null
7878

7979
@SuppressLint("ValidFragment")
8080
constructor(screenView: Screen) : super(screenView)

android/src/main/java/com/swmansion/rnscreens/bottomsheet/SheetDelegate.kt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,24 +175,16 @@ class SheetDelegate(
175175
}
176176

177177
is KeyboardVisible -> {
178-
val newMaxHeight =
179-
if (behavior.maxHeight - keyboardState.height > 1) {
180-
behavior.maxHeight - keyboardState.height
181-
} else {
182-
behavior.maxHeight
183-
}
184178
when (screen.sheetDetents.count()) {
185179
1 ->
186180
behavior.apply {
187-
useSingleDetent(height = newMaxHeight)
188181
addBottomSheetCallback(keyboardHandlerCallback)
189182
}
190183

191184
2 ->
192185
behavior.apply {
193186
useTwoDetents(
194187
state = BottomSheetBehavior.STATE_EXPANDED,
195-
secondHeight = newMaxHeight,
196188
)
197189
addBottomSheetCallback(keyboardHandlerCallback)
198190
}
@@ -202,7 +194,6 @@ class SheetDelegate(
202194
useThreeDetents(
203195
state = BottomSheetBehavior.STATE_EXPANDED,
204196
)
205-
maxHeight = newMaxHeight
206197
addBottomSheetCallback(keyboardHandlerCallback)
207198
}
208199

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.swmansion.rnscreens.ext
2+
3+
import androidx.fragment.app.Fragment
4+
import com.swmansion.rnscreens.ScreenStackFragment
5+
6+
internal fun Fragment.asScreenStackFragment() = this as ScreenStackFragment

0 commit comments

Comments
 (0)