From 3ec51f3291c34b253ece3cebd4d41622af8e5bc5 Mon Sep 17 00:00:00 2001 From: Noah Godel Date: Thu, 12 Mar 2026 16:54:52 +0100 Subject: [PATCH] Fix bottom sheet showing scroll position of triggering page When the shared WebView moves into a ScrollingView container (bottom sheet), its native scrollY carries over from the previous page. Turbo's JS performScroll() cannot reset it because a WRAP_CONTENT WebView inside a NestedScrollView is not scrollable from JS's perspective. Reset the native scroll in visitRendered(), after Turbo has updated history.restorationIdentifier to the current page's ID, so the resulting scroll event does not corrupt the previous page's saved scroll position. --- .../fragments/HotwireWebFragmentDelegate.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/navigation-fragments/src/main/java/dev/hotwire/navigation/fragments/HotwireWebFragmentDelegate.kt b/navigation-fragments/src/main/java/dev/hotwire/navigation/fragments/HotwireWebFragmentDelegate.kt index ba9a9a8..4cae75f 100644 --- a/navigation-fragments/src/main/java/dev/hotwire/navigation/fragments/HotwireWebFragmentDelegate.kt +++ b/navigation-fragments/src/main/java/dev/hotwire/navigation/fragments/HotwireWebFragmentDelegate.kt @@ -6,6 +6,7 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.activity.result.contract.ActivityResultContracts.RequestPermission import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult +import androidx.core.view.ScrollingView import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope @@ -231,6 +232,7 @@ internal class HotwireWebFragmentDelegate( override fun visitRendered() { callback.onVisitRendered(location) + resetWebViewScrollForScrollingContainer() navDestination.fragmentViewModel.setTitle(title()) removeTransitionalViews() } @@ -474,6 +476,19 @@ internal class HotwireWebFragmentDelegate( hotwireView?.webViewRefresh?.isEnabled = enabled } + /** + * When the WebView moves into a ScrollingView container (e.g., bottom + * sheet), its native scroll state carries over from the previous page + * and gets applied to the ScrollingView. Normally Turbo's JS would + * scroll to the top, but this has no effect on a WRAP_CONTENT WebView + * inside a ScrollingView. Reset it natively instead. + */ + private fun resetWebViewScrollForScrollingContainer() { + if (webView.parent is ScrollingView) { + webView.scrollTo(0, 0) + } + } + private fun removeTransitionalViews() { hotwireView?.webViewRefresh?.isRefreshing = false hotwireView?.errorRefresh?.isRefreshing = false