From 16855e4e99fbdd3c8ddac920b6c5a22642c62269 Mon Sep 17 00:00:00 2001 From: Evan Groth Date: Tue, 17 Mar 2026 14:31:58 -0400 Subject: [PATCH 1/4] fix pull to refresh --- .../example/jetnews/ui/home/HomeScreens.kt | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt index a46da2d96f..de79139d64 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt @@ -81,7 +81,10 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput @@ -96,6 +99,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import com.example.jetnews.R import com.example.jetnews.data.Result @@ -399,10 +403,48 @@ private fun LoadingContent( emptyContent() } else { val refreshState = rememberPullToRefreshState() + // Block pull-to-refresh when the current drag gesture scrolled the + // list before reaching the top. Only a fresh pull-down that begins + // while already at the top of the list should activate refresh. + val guardConnection = remember { + object : NestedScrollConnection { + var scrolledInCurrentGesture = false + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource, + ): Offset { + if (source == NestedScrollSource.UserInput) { + // The child consumed a downward scroll — the list + // was not yet at the top when the gesture started. + if (consumed.y > 0f) { + scrolledInCurrentGesture = true + } + // Consume overscroll so PullToRefreshBox never sees + // it when the gesture didn't start at the top. + if (scrolledInCurrentGesture && available.y > 0f) { + return available + } + } + return Offset.Zero + } + + override suspend fun onPreFling(available: Velocity): Velocity { + // Finger lifted — reset for the next gesture. + scrolledInCurrentGesture = false + return Velocity.Zero + } + } + } PullToRefreshBox( isRefreshing = loading, onRefresh = onRefresh, - content = { content() }, + content = { + Box(Modifier.fillMaxSize().nestedScroll(guardConnection)) { + content() + } + }, state = refreshState, indicator = { Indicator( From 6b1376baa55eddac7e52cf7d9fca30ee98df937e Mon Sep 17 00:00:00 2001 From: Evan Groth Date: Tue, 17 Mar 2026 16:27:34 -0400 Subject: [PATCH 2/4] remove comments --- .../main/java/com/example/jetnews/ui/home/HomeScreens.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt index de79139d64..5068f6d375 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt @@ -403,9 +403,6 @@ private fun LoadingContent( emptyContent() } else { val refreshState = rememberPullToRefreshState() - // Block pull-to-refresh when the current drag gesture scrolled the - // list before reaching the top. Only a fresh pull-down that begins - // while already at the top of the list should activate refresh. val guardConnection = remember { object : NestedScrollConnection { var scrolledInCurrentGesture = false @@ -416,13 +413,9 @@ private fun LoadingContent( source: NestedScrollSource, ): Offset { if (source == NestedScrollSource.UserInput) { - // The child consumed a downward scroll — the list - // was not yet at the top when the gesture started. if (consumed.y > 0f) { scrolledInCurrentGesture = true } - // Consume overscroll so PullToRefreshBox never sees - // it when the gesture didn't start at the top. if (scrolledInCurrentGesture && available.y > 0f) { return available } @@ -431,7 +424,6 @@ private fun LoadingContent( } override suspend fun onPreFling(available: Velocity): Velocity { - // Finger lifted — reset for the next gesture. scrolledInCurrentGesture = false return Velocity.Zero } From dba116773fdade088bb2dd84a96bc15a05b507c6 Mon Sep 17 00:00:00 2001 From: Evan Groth Date: Tue, 17 Mar 2026 16:28:03 -0400 Subject: [PATCH 3/4] Revert "remove comments" This reverts commit 6b1376baa55eddac7e52cf7d9fca30ee98df937e. --- .../main/java/com/example/jetnews/ui/home/HomeScreens.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt index 5068f6d375..de79139d64 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt @@ -403,6 +403,9 @@ private fun LoadingContent( emptyContent() } else { val refreshState = rememberPullToRefreshState() + // Block pull-to-refresh when the current drag gesture scrolled the + // list before reaching the top. Only a fresh pull-down that begins + // while already at the top of the list should activate refresh. val guardConnection = remember { object : NestedScrollConnection { var scrolledInCurrentGesture = false @@ -413,9 +416,13 @@ private fun LoadingContent( source: NestedScrollSource, ): Offset { if (source == NestedScrollSource.UserInput) { + // The child consumed a downward scroll — the list + // was not yet at the top when the gesture started. if (consumed.y > 0f) { scrolledInCurrentGesture = true } + // Consume overscroll so PullToRefreshBox never sees + // it when the gesture didn't start at the top. if (scrolledInCurrentGesture && available.y > 0f) { return available } @@ -424,6 +431,7 @@ private fun LoadingContent( } override suspend fun onPreFling(available: Velocity): Velocity { + // Finger lifted — reset for the next gesture. scrolledInCurrentGesture = false return Velocity.Zero } From 2f028b56d863d92018d00bb1b32e7803dc66fcdc Mon Sep 17 00:00:00 2001 From: Evan Groth Date: Tue, 17 Mar 2026 16:57:09 -0400 Subject: [PATCH 4/4] Use guard clause in onPostScroll for readability Early-return when the scroll source is not UserInput instead of wrapping the body in a conditional block. No behavior change. --- .../example/jetnews/ui/home/HomeScreens.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt index de79139d64..f2127adb9e 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt @@ -415,17 +415,17 @@ private fun LoadingContent( available: Offset, source: NestedScrollSource, ): Offset { - if (source == NestedScrollSource.UserInput) { - // The child consumed a downward scroll — the list - // was not yet at the top when the gesture started. - if (consumed.y > 0f) { - scrolledInCurrentGesture = true - } - // Consume overscroll so PullToRefreshBox never sees - // it when the gesture didn't start at the top. - if (scrolledInCurrentGesture && available.y > 0f) { - return available - } + if (source != NestedScrollSource.UserInput) return Offset.Zero + + // The child consumed a downward scroll — the list + // was not yet at the top when the gesture started. + if (consumed.y > 0f) { + scrolledInCurrentGesture = true + } + // Consume overscroll so PullToRefreshBox never sees + // it when the gesture didn't start at the top. + if (scrolledInCurrentGesture && available.y > 0f) { + return available } return Offset.Zero }