Skip to content
Prev Previous commit
Next Next commit
[Jetnews] Fix swipe to refresh using nested scrolling
  • Loading branch information
JolandaVerhoef committed Dec 15, 2020
commit 17c6687f7e6cb4b27f2a5f8c9f8a660b94e3e32e
80 changes: 71 additions & 9 deletions JetNews/app/src/main/java/com/example/jetnews/ui/SwipeToRefresh.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,21 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FractionalThreshold
import androidx.compose.material.SwipeableState
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.onCommit
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.gesture.nestedscroll.NestedScrollConnection
import androidx.compose.ui.gesture.nestedscroll.NestedScrollSource
import androidx.compose.ui.gesture.nestedscroll.nestedScroll
import androidx.compose.ui.gesture.scrollorientationlocking.Orientation
import androidx.compose.ui.platform.AmbientDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt

Expand All @@ -50,15 +56,17 @@ fun SwipeToRefreshLayout(
}

Box(
modifier = Modifier.swipeable(
state = state,
anchors = mapOf(
-refreshDistance to false,
refreshDistance to true
),
thresholds = { _, _ -> FractionalThreshold(0.5f) },
orientation = Orientation.Vertical
)
modifier = Modifier
.nestedScroll(state.PreUpPostDownNestedScrollConnection)
.swipeable(
state = state,
anchors = mapOf(
-refreshDistance to false,
refreshDistance to true
),
thresholds = { _, _ -> FractionalThreshold(0.5f) },
orientation = Orientation.Vertical
)
) {
content()
Box(
Expand All @@ -79,3 +87,57 @@ fun SwipeToRefreshLayout(
}
}
}

/**
* Temporary workaround for nested scrolling behavior. See b/174756744
*/
@ExperimentalMaterialApi
private val <T> SwipeableState<T>.PreUpPostDownNestedScrollConnection: NestedScrollConnection
get() = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.toFloat()
return if (delta < 0 && source == NestedScrollSource.Drag) {
performDrag(delta).toOffset()
} else {
Offset.Zero
}
}

override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
return if (source == NestedScrollSource.Drag) {
performDrag(available.toFloat()).toOffset()
} else {
Offset.Zero
}
}

override fun onPreFling(available: Velocity): Velocity {
val toFling = available.pixelsPerSecond.toFloat()
return if (toFling < 0) {
performFling(velocity = toFling) {}
// since we go to the anchor with tween settling, consume all for the best UX
available
} else {
Velocity.Zero
}
}

override fun onPostFling(
consumed: Velocity,
available: Velocity,
onFinished: (Velocity) -> Unit
) {
performFling(velocity = available.pixelsPerSecond.toFloat()) {
// since we go to the anchor with tween settling, consume all for the best UX
onFinished.invoke(available)
}
}

private fun Float.toOffset(): Offset = Offset(0f, this)

private fun Offset.toFloat(): Float = this.y
}