From bf83e4b5df497e9419e0197405a232447361d580 Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Thu, 3 Oct 2024 10:13:05 +0200 Subject: [PATCH] fix: prevent crash when there is no appearance model built (#2375) ## Description I haven't investigated it carefully, but there were case when updating `sheetCornerRadius` the appearance model has not been built yet leading to a crash when casting to a non-null type. Additionally, this PR prevents calls to `onSheetCornerRadiusUpdate` when not in `formSheet` presentation or not in native stack. ## Changes * Prevent calls to `onSheetCornerRadiusUpdate` when not in `formSheet` presentation. * Check for nullish view background before attempt to set the corner radius. * Update the corner radius after all updates are applied, so that the `stackPresentation` has change of being updated. ## Test code and steps to reproduce `Test1649`. Try setting the corner radius now, should work properly. ## Checklist - [x] Included code example that can be used to test this change - [ ] Ensured that CI passes --- .../java/com/swmansion/rnscreens/Screen.kt | 35 +++++++++++++++++-- .../rnscreens/ScreenStackFragment.kt | 8 +---- .../swmansion/rnscreens/ScreenViewManager.kt | 6 ++++ 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/com/swmansion/rnscreens/Screen.kt b/android/src/main/java/com/swmansion/rnscreens/Screen.kt index 0f91f963fd..b556d115ac 100644 --- a/android/src/main/java/com/swmansion/rnscreens/Screen.kt +++ b/android/src/main/java/com/swmansion/rnscreens/Screen.kt @@ -21,6 +21,9 @@ import com.facebook.react.uimanager.UIManagerModule import com.facebook.react.uimanager.events.EventDispatcher import com.facebook.react.views.scroll.ReactScrollView import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.shape.CornerFamily +import com.google.android.material.shape.MaterialShapeDrawable +import com.google.android.material.shape.ShapeAppearanceModel import com.swmansion.rnscreens.events.HeaderHeightChangeEvent import com.swmansion.rnscreens.events.SheetDetentChangedEvent @@ -54,10 +57,16 @@ class Screen( // Props for controlling modal presentation var isSheetGrabberVisible: Boolean = false + + // corner radius must be updated after all props prop updates from a single transaction + // have been applied, because it depends on the presentation type. + private var shouldUpdateSheetCornerRadius = false var sheetCornerRadius: Float = 0F set(value) { - field = value - (fragment as? ScreenStackFragment)?.onSheetCornerRadiusChange() + if (field != value) { + field = value + shouldUpdateSheetCornerRadius = true + } } var sheetExpandsWhenScrolledToEdge: Boolean = true @@ -420,6 +429,28 @@ class Screen( ) } + internal fun onFinalizePropsUpdate() { + if (shouldUpdateSheetCornerRadius) { + shouldUpdateSheetCornerRadius = false + onSheetCornerRadiusChange() + } + } + + internal fun onSheetCornerRadiusChange() { + if (stackPresentation !== StackPresentation.FORM_SHEET || background == null) { + return + } + (background as MaterialShapeDrawable?)?.let { + it.shapeAppearanceModel = + ShapeAppearanceModel + .Builder() + .apply { + setTopLeftCorner(CornerFamily.ROUNDED, sheetCornerRadius) + setTopRightCorner(CornerFamily.ROUNDED, sheetCornerRadius) + }.build() + } + } + enum class StackPresentation { PUSH, MODAL, diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt index f58c88d043..8fc46a9aea 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt @@ -198,13 +198,7 @@ class ScreenStackFragment : } internal fun onSheetCornerRadiusChange() { - (screen.background as MaterialShapeDrawable).shapeAppearanceModel = - ShapeAppearanceModel - .Builder() - .apply { - setTopLeftCorner(CornerFamily.ROUNDED, screen.sheetCornerRadius) - setTopRightCorner(CornerFamily.ROUNDED, screen.sheetCornerRadius) - }.build() + screen.onSheetCornerRadiusChange() } override fun onCreateView( diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt index 2b752266b9..48f82b9511 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt @@ -92,6 +92,12 @@ open class ScreenViewManager : return super.updateState(view, props, stateWrapper) } + // Called after all props are updated for given view + override fun onAfterUpdateTransaction(view: Screen) { + super.onAfterUpdateTransaction(view) + view.onFinalizePropsUpdate() + } + @ReactProp(name = "activityState") fun setActivityState( view: Screen,