Merge "Include pixel 4a (5G) in InvalidVideoProfilesQuirk" into androidx-main
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 8717a03..33b5d9d 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -975,14 +975,16 @@
}
@androidx.compose.foundation.ExperimentalFoundationApi public final class PagerDefaults {
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.pager.PagerSnapDistance pagerSnapDistance, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> lowVelocityAnimationSpec, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> highVelocityAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, optional float snapVelocityThreshold);
method public androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection(androidx.compose.foundation.gestures.Orientation orientation);
field public static final androidx.compose.foundation.pager.PagerDefaults INSTANCE;
}
public final class PagerKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void HorizontalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(androidx.compose.foundation.pager.PagerState state, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function1<? super java.lang.Integer,kotlin.Unit> pageContent);
}
@androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PagerSnapDistance {
@@ -994,40 +996,39 @@
method public androidx.compose.foundation.pager.PagerSnapDistance atMost(int pages);
}
- @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public final class PagerState implements androidx.compose.foundation.gestures.ScrollableState {
+ @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public abstract class PagerState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public PagerState(optional int initialPage, optional float initialPageOffsetFraction);
- method public suspend Object? animateScrollToPage(int page, optional float pageOffsetFraction, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public final suspend Object? animateScrollToPage(int page, optional float pageOffsetFraction, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
method public float dispatchRawDelta(float delta);
- method public int getCurrentPage();
- method public float getCurrentPageOffsetFraction();
- method public int getInitialPage();
- method public float getInitialPageOffsetFraction();
- method public androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
- method public int getSettledPage();
- method public int getTargetPage();
+ method public final boolean getCanScrollBackward();
+ method public final boolean getCanScrollForward();
+ method public final int getCurrentPage();
+ method public final float getCurrentPageOffsetFraction();
+ method public final int getInitialPage();
+ method public final float getInitialPageOffsetFraction();
+ method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+ method public abstract int getPageCount();
+ method public final int getSettledPage();
+ method public final int getTargetPage();
method public boolean isScrollInProgress();
method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
- method public suspend Object? scrollToPage(int page, optional float pageOffsetFraction, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
- property public boolean canScrollBackward;
- property public boolean canScrollForward;
+ method public final suspend Object? scrollToPage(int page, optional float pageOffsetFraction, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public final boolean canScrollBackward;
+ property public final boolean canScrollForward;
property public final int currentPage;
property public final float currentPageOffsetFraction;
property public final int initialPage;
property public final float initialPageOffsetFraction;
property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
property public boolean isScrollInProgress;
+ property public abstract int pageCount;
property public final int settledPage;
property public final int targetPage;
- field public static final androidx.compose.foundation.pager.PagerState.Companion Companion;
- }
-
- public static final class PagerState.Companion {
- method public androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.pager.PagerState,?> getSaver();
- property public final androidx.compose.runtime.saveable.Saver<androidx.compose.foundation.pager.PagerState,?> Saver;
}
public final class PagerStateKt {
- method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction);
+ method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction, kotlin.jvm.functions.Function0<java.lang.Integer> pageCount);
+ method @Deprecated @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.pager.PagerState rememberPagerState(optional int initialPage, optional float initialPageOffsetFraction);
}
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerCarrouselDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerCarrouselDemos.kt
index a55b0aa..5b43150 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerCarrouselDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerCarrouselDemos.kt
@@ -55,13 +55,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun HorizontalCarrouselDemo() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier,
state = pagerState,
- pageCount = PagesCount,
pageSize = PageSize.Fixed(200.dp)
) {
CarrouselItem(it, Orientation.Vertical)
@@ -73,13 +72,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun VerticalCarrouselDemo() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
VerticalPager(
modifier = Modifier.weight(0.9f),
state = pagerState,
- pageCount = PagesCount,
pageSize = PageSize.Fixed(200.dp)
) {
CarrouselItem(it, Orientation.Horizontal)
@@ -91,13 +89,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun HorizontalCustomPageSizeDemo() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier,
state = pagerState,
- pageCount = PagesCount,
pageSize = ThreePagesPerViewport,
pageSpacing = 8.dp
) {
@@ -110,13 +107,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun HorizontalCustomPageSizeWithCustomMaxScrollDemo() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier,
state = pagerState,
- pageCount = PagesCount,
pageSize = ThreePagesPerViewport,
pageSpacing = 8.dp,
flingBehavior = PagerDefaults.flingBehavior(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerDemos.kt
index 49473eb8..45a08b6 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerDemos.kt
@@ -72,11 +72,10 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun VerticalPagerDemo() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
VerticalPager(
modifier = Modifier.fillMaxSize(),
state = pagerState,
- pageCount = PagesCount
) {
PagerItem(it)
}
@@ -85,12 +84,11 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun HorizontalPagerDemo() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
HorizontalPager(
modifier = Modifier.fillMaxSize(),
state = pagerState,
- pageCount = PagesCount
) {
PagerItem(it)
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerStateInteractionsDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerStateInteractionsDemos.kt
index 48202c2..5922b08 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerStateInteractionsDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/pager/PagerStateInteractionsDemos.kt
@@ -46,13 +46,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun StateDrivenPage() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier.weight(0.9f),
- state = pagerState,
- pageCount = PagesCount
+ state = pagerState
) {
PagerItem(it)
}
@@ -63,13 +62,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun StateDrivenPageWithMonitor() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier.weight(0.8f),
- state = pagerState,
- pageCount = PagesCount
+ state = pagerState
) {
PagerItem(it)
}
@@ -81,12 +79,11 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun StateMonitoringPager() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier.weight(0.9f),
- state = pagerState,
- pageCount = PagesCount
+ state = pagerState
) {
PagerItem(it)
}
@@ -107,7 +104,7 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun StateMonitoringCustomPageSize() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { PagesCount }
val fling = PagerDefaults.flingBehavior(
state = pagerState, PagerSnapDistance.atMost(3)
@@ -117,7 +114,6 @@
HorizontalPager(
modifier = Modifier.weight(0.9f),
state = pagerState,
- pageCount = PagesCount,
pageSize = PageSize.Fixed(96.dp),
flingBehavior = fling
) {
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
index bed4f40..b95aeb5 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
@@ -30,8 +30,8 @@
import androidx.compose.foundation.pager.PageSize
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.material.Text
import androidx.compose.material.Button
+import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
@@ -47,9 +47,10 @@
@Composable
fun SimpleHorizontalPagerSample() {
// Creates a 1-pager/viewport horizontal pager with single page snapping
+ val state = rememberPagerState { 10 }
HorizontalPager(
+ state = state,
modifier = Modifier.fillMaxSize(),
- pageCount = 10
) { page ->
Box(
modifier = Modifier
@@ -69,9 +70,10 @@
@Composable
fun SimpleVerticalPagerSample() {
// Creates a 1-pager/viewport vertical pager with single page snapping
+ val state = rememberPagerState { 10 }
VerticalPager(
- modifier = Modifier.fillMaxSize(),
- pageCount = 10
+ state = state,
+ modifier = Modifier.fillMaxSize()
) { page ->
Box(
modifier = Modifier
@@ -91,11 +93,10 @@
@Composable
fun PagerWithStateSample() {
// You can use PagerState to define an initial page
- val state = rememberPagerState(initialPage = 5)
+ val state = rememberPagerState(initialPage = 5) { 10 }
HorizontalPager(
modifier = Modifier.fillMaxSize(),
- state = state,
- pageCount = 10
+ state = state
) { page ->
Box(
modifier = Modifier
@@ -129,9 +130,10 @@
}
}
+ val state = rememberPagerState { 10 }
HorizontalPager(
+ state = state,
modifier = Modifier.fillMaxSize(),
- pageCount = 10,
pageSize = CustomPageSize
) { page ->
Box(
@@ -151,12 +153,11 @@
@Sampled
@Composable
fun ObservingStateChangesInPagerStateSample() {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { 10 }
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
modifier = Modifier.weight(0.9f),
- state = pagerState,
- pageCount = 10
+ state = pagerState
) { page ->
Box(
modifier = Modifier
@@ -169,9 +170,11 @@
Text(text = page.toString(), fontSize = 32.sp)
}
}
- Column(modifier = Modifier
- .weight(0.1f)
- .fillMaxWidth()) {
+ Column(
+ modifier = Modifier
+ .weight(0.1f)
+ .fillMaxWidth()
+ ) {
Text(text = "Current Page: ${pagerState.currentPage}")
Text(text = "Target Page: ${pagerState.targetPage}")
Text(text = "Settled Page Offset: ${pagerState.settledPage}")
@@ -183,13 +186,12 @@
@Sampled
@Composable
fun AnimateScrollPageSample() {
- val state = rememberPagerState()
+ val state = rememberPagerState { 10 }
val animationScope = rememberCoroutineScope()
Column {
HorizontalPager(
modifier = Modifier.weight(0.7f),
- state = state,
- pageCount = 10
+ state = state
) { page ->
Box(
modifier = Modifier
@@ -203,7 +205,11 @@
}
}
- Box(modifier = Modifier.weight(0.3f).fillMaxWidth(), contentAlignment = Alignment.Center) {
+ Box(
+ modifier = Modifier
+ .weight(0.3f)
+ .fillMaxWidth(), contentAlignment = Alignment.Center
+ ) {
Button(onClick = {
animationScope.launch {
state.animateScrollToPage(state.currentPage + 1)
@@ -219,13 +225,12 @@
@Sampled
@Composable
fun ScrollToPageSample() {
- val state = rememberPagerState()
+ val state = rememberPagerState { 10 }
val scrollScope = rememberCoroutineScope()
Column {
HorizontalPager(
modifier = Modifier.height(400.dp),
- state = state,
- pageCount = 10
+ state = state
) { page ->
Box(
modifier = Modifier
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
index 514a0b9..1905bee 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/BasePagerTest.kt
@@ -23,6 +23,7 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.snapping.MinFlingVelocityDp
import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
@@ -69,6 +70,7 @@
lateinit var focusManager: FocusManager
lateinit var firstItemFocusRequester: FocusRequester
var composeView: View? = null
+ lateinit var pagerState: PagerState
fun TouchInjectionScope.swipeWithVelocityAcrossMainAxis(velocity: Float, delta: Float? = null) {
val end = if (delta == null) {
@@ -107,9 +109,10 @@
}
internal fun createPager(
- state: PagerState,
- modifier: Modifier = Modifier,
+ initialPage: Int = 0,
+ initialPageOffsetFraction: Float = 0f,
pageCount: () -> Int = { DefaultPageCount },
+ modifier: Modifier = Modifier,
offscreenPageLimit: Int = config.beyondBoundsPageCount,
pageSize: () -> PageSize = { PageSize.Fill },
userScrollEnabled: Boolean = true,
@@ -119,17 +122,22 @@
contentPadding: PaddingValues = config.mainAxisContentPadding,
pageSpacing: Dp = config.pageSpacing,
reverseLayout: Boolean = config.reverseLayout,
+ snapVelocityThreshold: Dp = MinFlingVelocityDp,
key: ((index: Int) -> Any)? = null,
pageContent: @Composable (page: Int) -> Unit = { Page(index = it) }
) {
rule.setContent {
+ val state = rememberPagerState(initialPage, initialPageOffsetFraction, pageCount).also {
+ pagerState = it
+ }
composeView = LocalView.current
focusManager = LocalFocusManager.current
val flingBehavior =
PagerDefaults.flingBehavior(
state = state,
- pagerSnapDistance = snappingPage
+ pagerSnapDistance = snappingPage,
+ snapVelocityThreshold = snapVelocityThreshold
)
CompositionLocalProvider(
LocalLayoutDirection provides config.layoutDirection,
@@ -142,7 +150,6 @@
.nestedScroll(nestedScrollConnection)
) {
HorizontalOrVerticalPager(
- pageCount = pageCount(),
state = state,
beyondBoundsPageCount = offscreenPageLimit,
modifier = modifier
@@ -259,8 +266,7 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun HorizontalOrVerticalPager(
- pageCount: Int,
- state: PagerState = rememberPagerState(),
+ state: PagerState = rememberPagerState(pageCount = { DefaultPageCount }),
modifier: Modifier = Modifier,
userScrollEnabled: Boolean = true,
reverseLayout: Boolean = false,
@@ -274,7 +280,6 @@
) {
if (vertical) {
VerticalPager(
- pageCount = pageCount,
state = state,
modifier = modifier,
userScrollEnabled = userScrollEnabled,
@@ -289,7 +294,6 @@
)
} else {
HorizontalPager(
- pageCount = pageCount,
state = state,
modifier = modifier,
userScrollEnabled = userScrollEnabled,
@@ -370,7 +374,3 @@
PaddingValues(start = 16.dp),
PaddingValues(end = 16.dp)
)
-
-open class SingleOrientationPagerTest(
- orientation: Orientation
-) : BasePagerTest(ParamConfig(orientation))
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt
index 1680009..3527b40 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/EmptyPagerTests.kt
@@ -33,10 +33,8 @@
@Test
fun checkNoPagesArePlaced() {
// Arrange
- val state = PagerState()
-
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize(), pageCount = { 0 })
+ createPager(pageCount = { 0 }, modifier = Modifier.fillMaxSize())
// Assert
rule.onNodeWithTag("0").assertDoesNotExist()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
index bd96983..6531eba 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageLayoutPositionOnScrollingTest.kt
@@ -38,8 +38,7 @@
@Test
fun swipeForwardAndBackward_verifyPagesAreLaidOutCorrectly() {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(modifier = Modifier.fillMaxSize())
val delta = pagerSize * 0.4f * scrollForwardSign
// Act and Assert - forward
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt
index eebddc6..988a695 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PageSizeTest.kt
@@ -36,10 +36,9 @@
@Test
fun pageSizeFill_onlySnappedItemIsDisplayed() {
// Arrange
- val state = PagerState(5)
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
// Assert
rule.onNodeWithTag("4").assertDoesNotExist()
@@ -51,7 +50,6 @@
@Test
fun pagerSizeCustom_visibleItemsAreWithinViewport() {
// Arrange
- val state = PagerState(5)
val pagerMode = object : PageSize {
override fun Density.calculateMainAxisPageSize(
availableSpace: Int,
@@ -63,7 +61,7 @@
// Act
createPager(
- state = state,
+ initialPage = 5,
modifier = Modifier.crossAxisSize(200.dp),
offscreenPageLimit = 0,
pageSize = { pagerMode }
@@ -71,14 +69,14 @@
// Assert
rule.runOnIdle {
- val visibleItems = state.layoutInfo.visiblePagesInfo.size
+ val visibleItems = pagerState.layoutInfo.visiblePagesInfo.size
val pageCount = with(rule.density) {
(pagerSize / (pageSize + config.pageSpacing.roundToPx()))
} + 1
Truth.assertThat(visibleItems).isEqualTo(pageCount)
}
- for (pageIndex in 5 until state.layoutInfo.visiblePagesInfo.size + 4) {
+ for (pageIndex in 5 until pagerState.layoutInfo.visiblePagesInfo.size + 4) {
confirmPageIsInCorrectPosition(5, pageIndex)
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
index ff8f42c..06cd075 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
@@ -52,24 +52,22 @@
@Test
fun accessibilityScroll_scrollToPage() {
- val state = PagerState()
- createPager(state, offscreenPageLimit = 1)
+ createPager(offscreenPageLimit = 1)
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(0) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(0) }
rule.onNodeWithTag("1").assertExists()
rule.onNodeWithTag("1").performScrollTo()
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(1) }
- rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isEqualTo(0.0f) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(1) }
+ rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
}
@Test
fun accessibilityPaging_animateScrollToPage() {
- val state = PagerState(initialPage = 5)
- createPager(state)
+ createPager(initialPage = 5, pageCount = { DefaultPageCount })
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(5) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(5) }
val actionBackward = if (vertical) {
android.R.id.accessibilityActionPageUp
@@ -86,8 +84,8 @@
}
// Go to the previous page
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(4) }
- rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isEqualTo(0.0f) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(4) }
+ rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
val actionForward = if (vertical) {
android.R.id.accessibilityActionPageDown
@@ -104,16 +102,15 @@
}
// Go to the next page
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(5) }
- rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isEqualTo(0.0f) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(5) }
+ rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
}
@Test
fun userScrollEnabledIsOff_shouldNotAllowPageAccessibilityActions() {
// Arrange
- val state = PagerState()
createPager(
- state = state,
+ pageCount = { DefaultPageCount },
userScrollEnabled = false,
modifier = Modifier.fillMaxSize()
)
@@ -129,23 +126,22 @@
@Test
fun focusScroll_forwardAndBackward_shouldGoToPage_pageShouldBeCorrectlyPlaced() {
// Arrange
- val state = PagerState()
- createPager(state)
+ createPager(pageCount = { DefaultPageCount })
rule.runOnIdle { firstItemFocusRequester.requestFocus() }
// Act: move forward
rule.runOnIdle { focusManager.moveFocus(FocusDirection.Next) }
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(1) }
- rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isEqualTo(0.0f) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(1) }
+ rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
// Act: move backward
rule.runOnIdle { focusManager.moveFocus(FocusDirection.Previous) }
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(0) }
- rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isEqualTo(0.0f) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(0) }
+ rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
}
private fun <T> SemanticsNodeInteraction.withSemanticsNode(block: SemanticsNode.() -> T): T {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt
index 66adc7b..3ca1e45 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentPaddingTest.kt
@@ -74,12 +74,10 @@
@Test
fun contentPaddingIsApplied() {
- val state = PagerState()
val containerSize = pageTotalSize * 2
val largePaddingSize = pageTotalSize
createPager(
- state = state,
modifier = Modifier
.requiredSize(containerSize)
.testTag(PagerTag),
@@ -104,7 +102,7 @@
.assertCrossAxisSizeIsEqualTo(containerSize - smallPaddingSize * 2)
.assertMainAxisSizeIsEqualTo(pageTotalSize)
- state.scrollBy(largePaddingSize)
+ pagerState.scrollBy(largePaddingSize)
rule.onNodeWithTag(PageTag)
.assertStartPositionInRootIsEqualTo(0.dp)
@@ -113,10 +111,7 @@
@Test
fun contentPaddingIsNotAffectingScrollPosition() {
- val state = PagerState()
-
createPager(
- state = state,
modifier = Modifier
.requiredSize(pageTotalSize * 2)
.testTag(PagerTag),
@@ -132,19 +127,17 @@
)
}
- state.assertScrollPosition(0, 0.dp)
+ pagerState.assertScrollPosition(0, 0.dp)
- state.scrollBy(pageTotalSize)
+ pagerState.scrollBy(pageTotalSize)
- state.assertScrollPosition(0, pageTotalSize)
+ pagerState.assertScrollPosition(0, pageTotalSize)
}
@Test
fun scrollForwardItemWithinStartPaddingDisplayed() {
- val state = PagerState()
val padding = pageTotalSize * 1.5f
createPager(
- state = state,
modifier = Modifier
.requiredSize(padding * 2 + pageTotalSize)
.testTag(PagerTag),
@@ -166,9 +159,9 @@
rule.onNodeWithTag("2")
.assertStartPositionInRootIsEqualTo(pageTotalSize * 2 + padding)
- state.scrollBy(padding)
+ pagerState.scrollBy(padding)
- state.assertScrollPosition(1, padding - pageTotalSize)
+ pagerState.assertScrollPosition(1, padding - pageTotalSize)
rule.onNodeWithTag("0")
.assertStartPositionInRootIsEqualTo(0.dp)
@@ -182,10 +175,8 @@
@Test
fun scrollBackwardItemWithinStartPaddingDisplayed() {
- val state = PagerState()
val padding = pageTotalSize * 1.5f
createPager(
- state = state,
modifier = Modifier
.requiredSize(padding * 2 + pageTotalSize)
.testTag(PagerTag),
@@ -200,10 +191,10 @@
)
}
- state.scrollBy(pageTotalSize * 3)
- state.scrollBy(-pageTotalSize * 1.5f)
+ pagerState.scrollBy(pageTotalSize * 3)
+ pagerState.scrollBy(-pageTotalSize * 1.5f)
- state.assertScrollPosition(1, pageTotalSize * 0.5f)
+ pagerState.assertScrollPosition(1, pageTotalSize * 0.5f)
rule.onNodeWithTag("0")
.assertStartPositionInRootIsEqualTo(pageTotalSize * 1.5f - padding)
@@ -217,10 +208,8 @@
@Test
fun scrollForwardTillTheEnd() {
- val state = PagerState()
val padding = pageTotalSize * 1.5f
createPager(
- state = state,
modifier = Modifier
.requiredSize(padding * 2 + pageTotalSize)
.testTag(PagerTag),
@@ -235,9 +224,9 @@
)
}
- state.scrollBy(pageTotalSize * 3)
+ pagerState.scrollBy(pageTotalSize * 3)
- state.assertScrollPosition(3, 0.dp)
+ pagerState.assertScrollPosition(3, 0.dp)
rule.onNodeWithTag("1")
.assertStartPositionInRootIsEqualTo(pageTotalSize - padding)
@@ -247,9 +236,9 @@
.assertStartPositionInRootIsEqualTo(pageTotalSize * 3 - padding)
// there are no space to scroll anymore, so it should change nothing
- state.scrollBy(10.dp)
+ pagerState.scrollBy(10.dp)
- state.assertScrollPosition(3, 0.dp)
+ pagerState.assertScrollPosition(3, 0.dp)
rule.onNodeWithTag("1")
.assertStartPositionInRootIsEqualTo(pageTotalSize - padding)
@@ -261,10 +250,8 @@
@Test
fun scrollForwardTillTheEndAndABitBack() {
- val state = PagerState()
val padding = pageTotalSize * 1.5f
createPager(
- state = state,
modifier = Modifier
.requiredSize(padding * 2 + pageTotalSize)
.testTag(PagerTag),
@@ -279,10 +266,10 @@
)
}
- state.scrollBy(pageTotalSize * 3)
- state.scrollBy(-pageTotalSize / 2)
+ pagerState.scrollBy(pageTotalSize * 3)
+ pagerState.scrollBy(-pageTotalSize / 2)
- state.assertScrollPosition(2, pageTotalSize / 2)
+ pagerState.assertScrollPosition(2, pageTotalSize / 2)
rule.onNodeWithTag("1")
.assertStartPositionInRootIsEqualTo(pageTotalSize * 1.5f - padding)
@@ -295,15 +282,16 @@
@Test
fun contentPaddingAndWrapContent() {
rule.setContent {
+ val state = rememberPagerState { 1 }
Box(modifier = Modifier.testTag(ContainerTag)) {
HorizontalOrVerticalPager(
+ state = state,
contentPadding = PaddingValues(
beforeContentCrossAxis = 2.dp,
beforeContent = 4.dp,
afterContentCrossAxis = 6.dp,
afterContent = 8.dp
),
- pageCount = 1,
pageSize = PageSize.Fixed(pageTotalSize)
) {
Spacer(
@@ -331,15 +319,16 @@
@Test
fun contentPaddingAndNoContent() {
rule.setContent {
+ val state = rememberPagerState { 0 }
Box(modifier = Modifier.testTag(ContainerTag)) {
HorizontalOrVerticalPager(
+ state = state,
contentPadding = PaddingValues(
beforeContentCrossAxis = 2.dp,
beforeContent = 4.dp,
afterContentCrossAxis = 6.dp,
afterContent = 8.dp
),
- pageCount = 0,
pageSize = PageSize.Fixed(pageTotalSize)
) { }
}
@@ -355,15 +344,16 @@
@Test
fun contentPaddingAndZeroSizedItem() {
rule.setContent {
+ val state = rememberPagerState { 1 }
Box(modifier = Modifier.testTag(ContainerTag)) {
HorizontalOrVerticalPager(
+ state = state,
contentPadding = PaddingValues(
beforeContentCrossAxis = 2.dp,
beforeContent = 4.dp,
afterContentCrossAxis = 6.dp,
afterContent = 8.dp
),
- pageCount = 1,
pageSize = PageSize.Fixed(0.dp)
) {
Box { }
@@ -383,10 +373,8 @@
val topPadding = pageTotalSize * 2
val bottomPadding = pageTotalSize / 2
val listSize = pageTotalSize * 3
- val state = PagerState()
createPager(
reverseLayout = true,
- state = state,
modifier = Modifier.requiredSize(listSize),
contentPadding = PaddingValues(
beforeContent = topPadding,
@@ -411,7 +399,7 @@
.assertStartPositionInRootIsEqualTo(-pageTotalSize / 2)
// Scroll to the top.
- state.scrollBy(pageTotalSize * 2.5f)
+ pagerState.scrollBy(pageTotalSize * 2.5f)
rule.onNodeWithTag("2").assertStartPositionInRootIsEqualTo(topPadding)
// Shouldn't be visible
@@ -424,10 +412,8 @@
val topPadding = pageTotalSize * 2
val bottomPadding = pageTotalSize * 2
val listSize = pageTotalSize * 3
- val state = PagerState()
createPager(
reverseLayout = true,
- state = state,
modifier = Modifier.requiredSize(listSize),
contentPadding = PaddingValues(
beforeContent = topPadding,
@@ -449,7 +435,7 @@
rule.onNodeWithTag("1").assertDoesNotExist()
// Scroll to the top.
- state.scrollBy(pageTotalSize * 5f)
+ pagerState.scrollBy(pageTotalSize * 5f)
rule.onNodeWithTag("2").assertStartPositionInRootIsEqualTo(topPadding)
// Shouldn't be visible
@@ -461,7 +447,7 @@
fun overscrollWithContentPadding() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 2 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -470,7 +456,6 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = smallPaddingSize),
- pageCount = 2,
pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
@@ -512,7 +497,7 @@
fun totalPaddingLargerParentSize_initialState() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState() { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -521,7 +506,6 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize),
- pageCount = 4,
pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
@@ -550,7 +534,7 @@
fun totalPaddingLargerParentSize_scrollByPadding() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -559,7 +543,6 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize),
- pageCount = 4,
pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
@@ -592,7 +575,7 @@
fun totalPaddingLargerParentSize_scrollToLastItem() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -601,7 +584,6 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize),
- pageCount = 4,
pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
@@ -634,7 +616,7 @@
fun totalPaddingLargerParentSize_scrollToLastItemByDelta() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -644,7 +626,6 @@
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize),
pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
) {
Box(
Modifier
@@ -677,7 +658,7 @@
// the whole end content padding is displayed
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -686,8 +667,7 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize),
- pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
+ pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
Modifier
@@ -716,7 +696,7 @@
fun eachPaddingLargerParentSize_initialState() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -725,8 +705,7 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
- pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
+ pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
Modifier
@@ -751,7 +730,7 @@
fun eachPaddingLargerParentSize_scrollByPadding() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -760,8 +739,7 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
- pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
+ pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
Modifier
@@ -793,7 +771,7 @@
fun eachPaddingLargerParentSize_scrollToLastItem() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -802,8 +780,7 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
- pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
+ pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
Modifier
@@ -838,7 +815,7 @@
fun eachPaddingLargerParentSize_scrollToLastItemByDelta() {
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -847,8 +824,7 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
- pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
+ pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
Modifier
@@ -884,7 +860,7 @@
// only the end content padding is displayed
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
Box(
modifier = Modifier
.testTag(ContainerTag)
@@ -893,8 +869,7 @@
HorizontalOrVerticalPager(
state = state,
contentPadding = PaddingValues(mainAxis = pageTotalSize * 2),
- pageSize = PageSize.Fixed(pageTotalSize),
- pageCount = 4
+ pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
Modifier
@@ -925,7 +900,7 @@
val padding = PaddingValues(start = 20.dp, end = 8.dp)
lateinit var state: PagerState
rule.setContent {
- state = rememberPagerState()
+ state = rememberPagerState { 4 }
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
HorizontalOrVerticalPager(
modifier = Modifier
@@ -933,7 +908,6 @@
.mainAxisSize(pageTotalSize * 2),
state = state,
contentPadding = padding,
- pageCount = 4,
pageSize = PageSize.Fixed(pageTotalSize)
) {
Box(
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt
index 2c60da4..4f02a20 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerContentTest.kt
@@ -16,10 +16,10 @@
package androidx.compose.foundation.pager
+import android.os.Build
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
-import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -30,31 +30,39 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
+import androidx.compose.testutils.assertPixels
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipe
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
+import androidx.test.filters.SdkSuppress
import com.google.common.truth.Truth
-import kotlin.test.assertContentEquals
import kotlin.test.assertTrue
+import org.junit.Rule
import org.junit.Test
-class PagerContentTest : SingleOrientationPagerTest(Orientation.Horizontal) {
+class PagerContentTest {
+
+ @get:Rule
+ val rule = createComposeRule()
@OptIn(ExperimentalFoundationApi::class)
@Test
fun pageContent_makeSureContainerOwnsOutsideModifiers() {
// Arrange
- val state = PagerState()
+ lateinit var state: PagerState
rule.setContent {
- HorizontalOrVerticalPager(
- pageCount = 10,
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { 10 }.also { state = it },
contentPadding = PaddingValues(horizontal = 32.dp),
pageSpacing = 4.dp,
modifier = Modifier
@@ -82,45 +90,51 @@
@OptIn(ExperimentalFoundationApi::class)
@Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
fun pageContent_makeSureInnerModifiersAreAppliedToPages() {
// Arrange
- val state = PagerState()
- val drawingList = mutableListOf<Int>()
+ val colors = listOf(Color.Blue, Color.Green, Color.Red)
rule.setContent {
- HorizontalOrVerticalPager(
- pageCount = 10,
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { colors.size },
modifier = Modifier
- .width(100.dp)
- .testTag("pager"),
- pageSize = PageSize.Fixed(10.dp)
+ .width(6.dp)
+ .testTag(PagerTestTag),
+ pageSize = PageSize.Fixed(2.dp)
) { page ->
+ val color = colors[page]
Box(
modifier = Modifier
- .background(Color.Black)
- .size(100.dp)
- .zIndex(if (page % 2 == 0) 100f else 50f)
- .drawWithContent {
- drawingList.add(page)
- }
.testTag(page.toString())
+ .width(2.dp)
+ .height(6.dp)
+ .zIndex(if (color == Color.Green) 1f else 0f)
+ .drawBehind {
+ drawRect(
+ color,
+ topLeft = Offset(-10.dp.toPx(), -10.dp.toPx()),
+ size = Size(20.dp.toPx(), 20.dp.toPx())
+ )
+ }
)
}
}
- rule.runOnIdle {
- assertContentEquals(drawingList, listOf(1, 3, 5, 7, 9, 0, 2, 4, 6, 8))
- }
+ rule.onNodeWithTag(PagerTestTag)
+ .captureToImage()
+ .assertPixels { Color.Green }
}
@OptIn(ExperimentalFoundationApi::class)
@Test
fun scrollableState_isScrollableWhenChangingPages() {
val states = mutableMapOf<Int, ScrollState>()
- val pagerState = PagerState()
+ val pagerState = PagerStateImpl(
+ initialPage = 0,
+ initialPageOffsetFraction = 0.0f,
+ updatedPageCount = { 2 })
rule.setContent {
HorizontalPager(
- pageCount = 2,
state = pagerState,
modifier = Modifier
.testTag("pager")
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt
index 33cc710..e9c8ddf 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerCrossAxisTest.kt
@@ -54,8 +54,7 @@
rule.setContent {
InfiniteAxisRootComposable {
HorizontalOrVerticalPager(
- pageCount = DefaultPageCount,
- state = rememberPagerState(),
+ state = rememberPagerState(pageCount = { DefaultPageCount }),
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
index a2b44b83..b9975e5 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
@@ -57,10 +57,7 @@
@Test
fun visiblePagesAreCorrect() {
- val state = PagerState()
-
createPager(
- state,
modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
pageCount = { 5 },
pageSize = { PageSize.Fixed(pageSizeDp) }
@@ -68,16 +65,13 @@
Box(Modifier.requiredSize(pageSizeDp))
}
rule.runOnIdle {
- state.layoutInfo.assertVisiblePages(count = 4)
+ pagerState.layoutInfo.assertVisiblePages(count = 4)
}
}
@Test
fun visiblePagesAreCorrectAfterScroll() {
- val state = PagerState()
-
createPager(
- state,
modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
pageCount = { 5 },
pageSize = { PageSize.Fixed(pageSizeDp) }
@@ -87,11 +81,11 @@
rule.runOnIdle {
runBlocking {
- state.scrollToPage(1)
- state.scrollBy(10f)
+ pagerState.scrollToPage(1)
+ pagerState.scrollBy(10f)
}
- state.layoutInfo.assertVisiblePages(
+ pagerState.layoutInfo.assertVisiblePages(
count = 4,
startIndex = 1,
startOffset = -10
@@ -101,10 +95,7 @@
@Test
fun visiblePagesAreCorrectWithSpacing() {
- val state = PagerState()
-
createPager(
- state,
modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
pageCount = { 5 },
pageSpacing = pageSizeDp,
@@ -114,22 +105,20 @@
}
rule.runOnIdle {
- state.layoutInfo.assertVisiblePages(count = 2, spacing = pageSizePx)
+ pagerState.layoutInfo.assertVisiblePages(count = 2, spacing = pageSizePx)
}
}
@Test
fun visiblePagesAreObservableWhenWeScroll() {
- val state = PagerState()
val currentInfo = StableRef<PagerLayoutInfo?>(null)
createPager(
- state,
modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
pageCount = { 5 },
pageSize = { PageSize.Fixed(pageSizeDp) },
additionalContent = {
- LaunchedEffect(key1 = state) {
- snapshotFlow { state.layoutInfo }.collect {
+ LaunchedEffect(key1 = pagerState) {
+ snapshotFlow { pagerState.layoutInfo }.collect {
currentInfo.value = it
}
}
@@ -142,7 +131,7 @@
// empty it here and scrolling should invoke observingFun again
currentInfo.value = null
runBlocking {
- state.scrollToPage(1)
+ pagerState.scrollToPage(1)
}
}
@@ -154,17 +143,15 @@
@Test
fun visiblePagesAreObservableWhenResize() {
- val state = PagerState()
var pageSize by mutableStateOf(PageSize.Fixed(pageSizeDp * 2))
var currentInfo: PagerLayoutInfo? = null
@Composable
fun observingFun() {
- currentInfo = state.layoutInfo
+ currentInfo = pagerState.layoutInfo
}
createPager(
- state,
pageCount = { 1 },
pageSize = { pageSize },
additionalContent = { observingFun() }
@@ -190,10 +177,7 @@
@Test
fun totalCountIsCorrect() {
var count by mutableStateOf(10)
- val state = PagerState()
-
createPager(
- state,
pageCount = { count },
pageSize = { PageSize.Fixed(10.dp) }
) {
@@ -201,12 +185,12 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.pagesCount).isEqualTo(10)
+ assertThat(pagerState.layoutInfo.pagesCount).isEqualTo(10)
count = 20
}
rule.runOnIdle {
- assertThat(state.layoutInfo.pagesCount).isEqualTo(20)
+ assertThat(pagerState.layoutInfo.pagesCount).isEqualTo(20)
}
}
@@ -214,10 +198,7 @@
fun viewportOffsetsAndSizeAreCorrect() {
val sizePx = 45
val sizeDp = with(rule.density) { sizePx.toDp() }
- val state = PagerState()
-
createPager(
- state,
modifier = Modifier
.mainAxisSize(sizeDp)
.crossAxisSize(sizeDp * 2),
@@ -228,9 +209,9 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(0)
- assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx)
- assertThat(state.layoutInfo.viewportSize).isEqualTo(
+ assertThat(pagerState.layoutInfo.viewportStartOffset).isEqualTo(0)
+ assertThat(pagerState.layoutInfo.viewportEndOffset).isEqualTo(sizePx)
+ assertThat(pagerState.layoutInfo.viewportSize).isEqualTo(
if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
)
}
@@ -249,10 +230,8 @@
val afterContentPaddingDp = with(rule.density) {
if (!reverseLayout) endPaddingPx.toDp() else startPaddingPx.toDp()
}
- val state = PagerState()
createPager(
- state,
modifier = Modifier
.mainAxisSize(sizeDp)
.crossAxisSize(sizeDp * 2),
@@ -269,10 +248,10 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
- assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
- assertThat(state.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
- assertThat(state.layoutInfo.viewportSize).isEqualTo(
+ assertThat(pagerState.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
+ assertThat(pagerState.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+ assertThat(pagerState.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
+ assertThat(pagerState.layoutInfo.viewportSize).isEqualTo(
if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
)
}
@@ -280,10 +259,7 @@
@Test
fun emptyPagesInVisiblePagesInfo() {
- val state = PagerState()
-
createPager(
- state,
pageCount = { 2 },
pageSize = { PageSize.Fixed(pageSizeDp) }
) {
@@ -291,9 +267,9 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.visiblePagesInfo.size).isEqualTo(2)
- assertThat(state.layoutInfo.visiblePagesInfo.first().index).isEqualTo(0)
- assertThat(state.layoutInfo.visiblePagesInfo.last().index).isEqualTo(1)
+ assertThat(pagerState.layoutInfo.visiblePagesInfo.size).isEqualTo(2)
+ assertThat(pagerState.layoutInfo.visiblePagesInfo.first().index).isEqualTo(0)
+ assertThat(pagerState.layoutInfo.visiblePagesInfo.last().index).isEqualTo(1)
}
}
@@ -310,10 +286,8 @@
val afterContentPaddingDp = with(rule.density) {
if (!reverseLayout) endPaddingPx.toDp() else startPaddingPx.toDp()
}
- val state = PagerState()
createPager(
- state,
modifier = Modifier
.mainAxisSize(sizeDp)
.crossAxisSize(sizeDp * 2),
@@ -328,11 +302,11 @@
) {}
rule.runOnIdle {
- assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
- assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
- assertThat(state.layoutInfo.beforeContentPadding).isEqualTo(startPaddingPx)
- assertThat(state.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
- assertThat(state.layoutInfo.viewportSize).isEqualTo(
+ assertThat(pagerState.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
+ assertThat(pagerState.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+ assertThat(pagerState.layoutInfo.beforeContentPadding).isEqualTo(startPaddingPx)
+ assertThat(pagerState.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
+ assertThat(pagerState.layoutInfo.viewportSize).isEqualTo(
if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
)
}
@@ -351,10 +325,8 @@
val afterContentPaddingDp = with(rule.density) {
if (!reverseLayout) endPaddingPx.toDp() else startPaddingPx.toDp()
}
- val state = PagerState()
createPager(
- state,
modifier = Modifier
.mainAxisSize(sizeDp)
.crossAxisSize(sizeDp * 2),
@@ -371,11 +343,11 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
- assertThat(state.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
- assertThat(state.layoutInfo.beforeContentPadding).isEqualTo(startPaddingPx)
- assertThat(state.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
- assertThat(state.layoutInfo.viewportSize).isEqualTo(
+ assertThat(pagerState.layoutInfo.viewportStartOffset).isEqualTo(-startPaddingPx)
+ assertThat(pagerState.layoutInfo.viewportEndOffset).isEqualTo(sizePx - startPaddingPx)
+ assertThat(pagerState.layoutInfo.beforeContentPadding).isEqualTo(startPaddingPx)
+ assertThat(pagerState.layoutInfo.afterContentPadding).isEqualTo(endPaddingPx)
+ assertThat(pagerState.layoutInfo.viewportSize).isEqualTo(
if (vertical) IntSize(sizePx * 2, sizePx) else IntSize(sizePx, sizePx * 2)
)
}
@@ -383,10 +355,7 @@
@Test
fun reverseLayoutIsCorrect() {
- val state = PagerState()
-
createPager(
- state,
modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
pageCount = { 5 },
pageSize = { PageSize.Fixed(pageSizeDp) }
@@ -395,17 +364,13 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.reverseLayout).isEqualTo(param.reverseLayout)
+ assertThat(pagerState.layoutInfo.reverseLayout).isEqualTo(param.reverseLayout)
}
}
@Test
fun orientationIsCorrect() {
-
- val state = PagerState()
-
createPager(
- state,
modifier = Modifier.requiredSize(pageSizeDp * 3.5f),
pageCount = { 5 },
pageSize = { PageSize.Fixed(pageSizeDp) }
@@ -414,7 +379,7 @@
}
rule.runOnIdle {
- assertThat(state.layoutInfo.orientation)
+ assertThat(pagerState.layoutInfo.orientation)
.isEqualTo(if (vertical) Orientation.Vertical else Orientation.Horizontal)
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
index ffa4aed..78b2758 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerNestedScrollContentTest.kt
@@ -20,7 +20,6 @@
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyList
@@ -35,10 +34,10 @@
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.test.filters.LargeTest
-import org.junit.Assert.assertEquals
import com.google.common.truth.Truth.assertThat
import kotlin.math.absoluteValue
import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -54,8 +53,7 @@
@Test
fun nestedScrollContent_shouldNotPropagateUnconsumedFlings() {
// Arrange
- val pagerState = PagerState()
- createPager(pagerState) {
+ createPager(pageCount = { DefaultPageCount }) {
LazyList(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(0.dp),
@@ -93,7 +91,6 @@
@Test
fun nestedScrollContent_shouldPropagateCrossAxisUnconsumedFlings() {
// Arrange
- val pagerState = PagerState()
var postFlingVelocity = Velocity.Zero
val dataCapturingConnection = object : NestedScrollConnection {
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
@@ -101,7 +98,10 @@
return Velocity.Zero
}
}
- createPager(pagerState, nestedScrollConnection = dataCapturingConnection) {
+ createPager(
+ pageCount = { DefaultPageCount },
+ nestedScrollConnection = dataCapturingConnection
+ ) {
LazyList(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(0.dp),
@@ -141,9 +141,8 @@
@Test
fun nestedScrollContent_shouldPropagateScrollCorrectly() {
// Arrange
- val pagerState = PagerState()
val lazyListState = LazyListState(9)
- createPager(pagerState) {
+ createPager(pageCount = { DefaultPageCount }) {
LazyList(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(0.dp),
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
index 4e93b07..aaf1a6f 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerOffscreenPageLimitPlacingTest.kt
@@ -36,8 +36,11 @@
@Test
fun offscreenPageLimitIsUsed_shouldPlaceMoreItemsThanVisibleOnesAsWeScroll() {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize(), offscreenPageLimit = 1)
+ createPager(
+ pageCount = { DefaultPageCount },
+ modifier = Modifier.fillMaxSize(),
+ offscreenPageLimit = 1
+ )
val delta = pagerSize * 1.4f * scrollForwardSign
repeat(DefaultAnimationRepetition) {
@@ -50,25 +53,29 @@
// Next page was placed
rule.runOnIdle {
Truth.assertThat(placed).contains(
- (state.currentPage + 1)
+ (pagerState.currentPage + 1)
.coerceAtMost(DefaultPageCount - 1)
)
}
}
rule.waitForIdle()
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun offscreenPageLimitIsUsed_shouldPlaceMoreItemsThanVisibleOnes() {
// Arrange
val initialIndex = 5
- val state = PagerState(initialIndex)
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize(), offscreenPageLimit = 2)
- val firstVisible = state.layoutInfo.visiblePagesInfo.first().index
- val lastVisible = state.layoutInfo.visiblePagesInfo.last().index
+ createPager(
+ initialPage = initialIndex,
+ pageCount = { DefaultPageCount },
+ modifier = Modifier.fillMaxSize(),
+ offscreenPageLimit = 2
+ )
+ val firstVisible = pagerState.layoutInfo.visiblePagesInfo.first().index
+ val lastVisible = pagerState.layoutInfo.visiblePagesInfo.last().index
// Assert
rule.runOnIdle {
Truth.assertThat(placed).contains(firstVisible - 2)
@@ -85,15 +92,19 @@
@Test
fun offscreenPageLimitIsNotUsed_shouldNotPlaceMoreItemsThanVisibleOnes() {
// Arrange
- val state = PagerState(5)
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize(), offscreenPageLimit = 0)
+ createPager(
+ initialPage = 5,
+ pageCount = { DefaultPageCount },
+ modifier = Modifier.fillMaxSize(),
+ offscreenPageLimit = 0
+ )
// Assert
rule.waitForIdle()
- val firstVisible = state.layoutInfo.visiblePagesInfo.first().index
- val lastVisible = state.layoutInfo.visiblePagesInfo.last().index
+ val firstVisible = pagerState.layoutInfo.visiblePagesInfo.first().index
+ val lastVisible = pagerState.layoutInfo.visiblePagesInfo.last().index
Truth.assertThat(placed).doesNotContain(firstVisible - 1)
Truth.assertThat(placed).contains(5)
Truth.assertThat(placed).doesNotContain(lastVisible + 1)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
index a56661e..abd50db 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPinnableContainerTest.kt
@@ -18,10 +18,10 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
-import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.list.assertIsNotPlaced
import androidx.compose.foundation.lazy.list.assertIsPlaced
import androidx.compose.runtime.Composable
@@ -37,6 +37,7 @@
import androidx.compose.ui.layout.PinnableContainer.PinnedHandle
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -44,18 +45,22 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
@OptIn(ExperimentalFoundationApi::class)
@MediumTest
-class PagerPinnableContainerTest :
- SingleOrientationPagerTest(orientation = Orientation.Horizontal) {
+class PagerPinnableContainerTest {
+
+ @get:Rule
+ val rule = createComposeRule()
private var pinnableContainer: PinnableContainer? = null
private var pageSizeDp = Dp.Unspecified
private val composed = mutableSetOf<Int>()
+ private lateinit var pagerState: PagerState
@Before
fun setup() {
@@ -81,13 +86,11 @@
@Test
fun pinnedPageIsComposedAndPlacedWhenScrolledOut() {
- val state = PagerState()
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { 100 }.also { pagerState = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 1) {
@@ -104,7 +107,7 @@
rule.runOnIdle {
assertThat(composed).contains(1)
runBlocking {
- state.scrollToPage(3)
+ pagerState.scrollToPage(3)
}
}
@@ -126,13 +129,11 @@
@Test
fun pagesBetweenPinnedAndCurrentVisibleAreNotComposed() {
- val state = PagerState()
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { 100 }.also { pagerState = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 1) {
@@ -148,7 +149,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollToPage(4)
+ pagerState.scrollToPage(4)
}
}
@@ -168,13 +169,11 @@
@Test
fun pinnedPageAfterVisibleOnesIsComposedAndPlacedWhenScrolledOut() {
- val state = PagerState()
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { 100 }.also { pagerState = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 4) {
@@ -186,7 +185,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollToPage(4)
+ pagerState.scrollToPage(4)
}
}
@@ -202,7 +201,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollToPage(0)
+ pagerState.scrollToPage(0)
}
}
@@ -223,13 +222,11 @@
@Test
fun pinnedPageCanBeUnpinned() {
- val state = PagerState()
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { 100 }.also { pagerState = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 1) {
@@ -245,7 +242,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollToPage(3)
+ pagerState.scrollToPage(3)
}
}
@@ -269,11 +266,10 @@
@Test
fun pinnedPageIsStillPinnedWhenReorderedAndNotVisibleAnymore() {
- val state = PagerState()
var list by mutableStateOf(listOf(0, 1, 2, 3, 4))
// Arrange.
rule.setContent {
- Pager(state, list, 2, 3)
+ Pager(list, 2, 3)
}
rule.runOnIdle {
@@ -299,11 +295,10 @@
}
@Composable
- fun Pager(state: PagerState, dataset: List<Int>, pinnedPage: Int, visiblePages: Int) {
- HorizontalOrVerticalPager(
- state = state,
- modifier = Modifier.mainAxisSize(pageSizeDp * visiblePages),
- pageCount = dataset.size,
+ fun Pager(dataset: List<Int>, pinnedPage: Int, visiblePages: Int) {
+ HorizontalPager(
+ state = rememberPagerState { dataset.size },
+ modifier = Modifier.width(pageSizeDp * visiblePages),
pageSize = PageSize.Fixed(pageSizeDp),
key = { dataset[it] }
) { page ->
@@ -316,13 +311,17 @@
@Test
fun unpinnedWhenPagerStateChanges() {
- var state by mutableStateOf(PagerState(initialPage = 2))
+ var state by mutableStateOf(
+ PagerStateImpl(
+ initialPage = 2,
+ initialPageOffsetFraction = 0f,
+ updatedPageCount = { 100 })
+ )
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
+ HorizontalPager(
state = state,
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 2) {
@@ -350,7 +349,10 @@
rule.runOnIdle {
assertThat(composed).contains(2)
- state = PagerState()
+ state = PagerStateImpl(
+ initialPage = 0,
+ initialPageOffsetFraction = 0f,
+ updatedPageCount = { 100 })
}
rule.waitUntil {
@@ -364,13 +366,17 @@
@Test
fun pinAfterPagerStateChange() {
- var state by mutableStateOf(PagerState())
+ var state by mutableStateOf(
+ PagerStateImpl(
+ initialPage = 0,
+ initialPageOffsetFraction = 0f,
+ updatedPageCount = { 100 })
+ )
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
+ HorizontalPager(
state = state,
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 0) {
@@ -381,7 +387,10 @@
}
rule.runOnIdle {
- state = PagerState()
+ state = PagerStateImpl(
+ initialPage = 0,
+ initialPageOffsetFraction = 0f,
+ updatedPageCount = { 100 })
}
rule.runOnIdle {
@@ -407,13 +416,12 @@
@Test
fun pagesArePinnedBasedOnGlobalIndexes() {
- val state = PagerState(initialPage = 3)
// Arrange.
+ lateinit var state: PagerState
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState(initialPage = 3) { 100 }.also { state = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 3) {
@@ -451,14 +459,13 @@
@Test
fun pinnedPageIsRemovedWhenNotVisible() {
- val state = PagerState(initialPage = 3)
+ lateinit var state: PagerState
var pageCount by mutableStateOf(10)
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState(initialPage = 3) { pageCount }.also { state = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = pageCount,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 3) {
@@ -496,11 +503,10 @@
@Test
fun pinnedPageIsRemovedWhenVisible() {
- val state = PagerState()
var pages by mutableStateOf(listOf(0, 1, 2))
// Arrange.
rule.setContent {
- Pager(state = state, dataset = pages, pinnedPage = 1, visiblePages = 2)
+ Pager(dataset = pages, pinnedPage = 1, visiblePages = 2)
}
rule.runOnIdle {
@@ -522,13 +528,11 @@
@Test
fun pinnedMultipleTimes() {
- val state = PagerState(0)
// Arrange.
rule.setContent {
- HorizontalOrVerticalPager(
- state = state,
+ HorizontalPager(
+ state = rememberPagerState { 100 }.also { pagerState = it },
modifier = Modifier.size(pageSizeDp * 2),
- pageCount = 100,
pageSize = PageSize.Fixed(pageSizeDp)
) { page ->
if (page == 1) {
@@ -549,7 +553,7 @@
handles.add(requireNotNull(pinnableContainer).pin())
assertThat(composed).contains(0)
runBlocking {
- state.scrollToPage(3)
+ pagerState.scrollToPage(3)
}
}
@@ -583,8 +587,8 @@
// Arrange.
rule.setContent {
CompositionLocalProvider(LocalPinnableContainer provides parentContainer) {
- HorizontalOrVerticalPager(
- pageCount = 1,
+ HorizontalPager(
+ state = rememberPagerState { 1 },
pageSize = PageSize.Fixed(pageSizeDp)
) {
pinnableContainer = LocalPinnableContainer.current
@@ -627,8 +631,8 @@
// Arrange.
rule.setContent {
CompositionLocalProvider(LocalPinnableContainer provides parentContainer) {
- HorizontalOrVerticalPager(
- pageCount = 1,
+ HorizontalPager(
+ state = rememberPagerState { 1 },
pageSize = PageSize.Fixed(pageSizeDp)
) {
pinnableContainer = LocalPinnableContainer.current
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
index cedcfd6..18ab68b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerPrefetcherTest.kt
@@ -52,7 +52,6 @@
var pageSizePx = 30
val pageSizeDp = with(rule.density) { pageSizePx.toDp() }
- lateinit var state: PagerState
@Test
fun notPrefetchingForwardInitially() {
@@ -76,7 +75,7 @@
val preFetchIndex = 2
rule.runOnIdle {
runBlocking {
- state.scrollBy(5f)
+ pagerState.scrollBy(5f)
}
}
@@ -94,7 +93,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(-5f)
+ pagerState.scrollBy(-5f)
}
}
@@ -113,7 +112,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(5f)
+ pagerState.scrollBy(5f)
}
}
var prefetchIndex = initialIndex + 2
@@ -126,8 +125,8 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(-2f)
- state.scrollBy(-1f)
+ pagerState.scrollBy(-2f)
+ pagerState.scrollBy(-1f)
}
}
@@ -146,7 +145,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(5f)
+ pagerState.scrollBy(5f)
}
}
@@ -154,8 +153,8 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(pageSizePx / 2f)
- state.scrollBy(pageSizePx / 2f)
+ pagerState.scrollBy(pageSizePx / 2f)
+ pagerState.scrollBy(pageSizePx / 2f)
}
}
@@ -177,7 +176,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(-5f)
+ pagerState.scrollBy(-5f)
}
}
@@ -185,8 +184,8 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(-pageSizePx / 2f)
- state.scrollBy(-pageSizePx / 2f)
+ pagerState.scrollBy(-pageSizePx / 2f)
+ pagerState.scrollBy(-pageSizePx / 2f)
}
}
@@ -207,7 +206,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(5f)
+ pagerState.scrollBy(5f)
}
}
@@ -222,8 +221,8 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(-2f)
- state.scrollBy(-1f)
+ pagerState.scrollBy(-2f)
+ pagerState.scrollBy(-1f)
}
}
@@ -259,7 +258,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(5f)
+ pagerState.scrollBy(5f)
}
}
@@ -273,7 +272,7 @@
rule.runOnIdle {
runBlocking {
- state.scrollBy(-2f)
+ pagerState.scrollBy(-2f)
}
}
@@ -298,11 +297,10 @@
) { constraints ->
val placeable = if (emit) {
subcompose(Unit) {
- state = rememberPagerState()
+ pagerState = rememberPagerState { 1000 }
HorizontalOrVerticalPager(
modifier = Modifier.mainAxisSize(pageSizeDp * 1.5f),
- state = state,
- pageCount = 1000
+ state = pagerState
) {
Spacer(
Modifier
@@ -328,7 +326,7 @@
rule.runOnIdle {
// this will schedule the prefetching
runBlocking(AutoTestFrameClock()) {
- state.scrollBy(pageSize.toFloat())
+ pagerState.scrollBy(pageSize.toFloat())
}
// then we synchronously dispose LazyColumn
emit = false
@@ -342,11 +340,10 @@
fun snappingToOtherPositionWhilePrefetchIsScheduled() {
val composedItems = mutableListOf<Int>()
rule.setContent {
- state = rememberPagerState()
+ pagerState = rememberPagerState { 1000 }
HorizontalOrVerticalPager(
modifier = Modifier.mainAxisSize(pageSizeDp * 1.5f),
- state = state,
- pageCount = 1000
+ state = pagerState
) {
composedItems.add(it)
Spacer(
@@ -367,10 +364,10 @@
runBlocking(AutoTestFrameClock()) {
// this will move the viewport so pages 1 and 2 are visible
// and schedule a prefetching for 3
- state.scrollBy(pageSize.toFloat())
+ pagerState.scrollBy(pageSize.toFloat())
// then we move so that pages 100 and 101 are visible.
// this should cancel the prefetch for 3
- state.scrollToPage(100)
+ pagerState.scrollToPage(100)
}
}
@@ -393,14 +390,14 @@
runBlocking(AutoTestFrameClock()) {
// this will move the viewport so pages 1-2 are visible
// and schedule a prefetching for 3
- state.scrollBy(pageSizePx.toFloat())
+ pagerState.scrollBy(pageSizePx.toFloat())
// move viewport by screen size to pages 4-5, so page 3 is just behind
// the first visible page
- state.scrollBy(pageSizePx * 3f)
+ pagerState.scrollBy(pageSizePx * 3f)
// move scroll further to pages 5-6, so page 3 is reused
- state.scrollBy(pageSizePx.toFloat())
+ pagerState.scrollBy(pageSizePx.toFloat())
}
}
@@ -409,7 +406,7 @@
rule.runOnIdle {
runBlocking(AutoTestFrameClock()) {
// scroll again to ensure page 3 was dropped
- state.scrollBy(pageSizePx * 100f)
+ pagerState.scrollBy(pageSizePx * 100f)
}
}
@@ -439,16 +436,13 @@
reverseLayout: Boolean = false,
contentPadding: PaddingValues = PaddingValues(0.dp)
) {
- state = PagerState(
- initialPage = initialPage,
- initialPageOffsetFraction = initialPageOffsetFraction
- )
createPager(
- state = state,
modifier = Modifier.mainAxisSize(pageSizeDp * 1.5f),
reverseLayout = reverseLayout,
contentPadding = contentPadding,
offscreenPageLimit = paramConfig.beyondBoundsPageCount,
+ initialPage = initialPage,
+ initialPageOffsetFraction = initialPageOffsetFraction,
pageCount = { 100 },
pageSize = {
object : PageSize {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
index 14eabb4..3d81073 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerScrollingTest.kt
@@ -39,10 +39,12 @@
) : BasePagerTest(config) {
@Test
- fun swipeWithLowVelocity_shouldBounceBack() {
+ fun swipeWithLowVelocity_defaultVelocityThreshold_shouldBounceBack() {
// Arrange
- val state = PagerState(5)
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(
+ initialPage = 5,
+ modifier = Modifier.fillMaxSize()
+ )
val delta = pagerSize * 0.4f * scrollForwardSign
// Act - forward
@@ -73,10 +75,52 @@
}
@Test
- fun swipeWithHighVelocity_shouldGoToNextPage() {
+ fun swipeWithLowVelocity_customVelocityThreshold_shouldBounceBack() {
// Arrange
val state = PagerState(5)
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ val snapVelocityThreshold = 200.dp
+ createPager(
+ state = state,
+ modifier = Modifier.fillMaxSize(),
+ snapVelocityThreshold = snapVelocityThreshold
+ )
+ val delta = pagerSize * 0.4f * scrollForwardSign
+
+ // Act - forward
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
+ delta
+ )
+ }
+ rule.waitForIdle()
+
+ // Assert
+ rule.onNodeWithTag("5").assertIsDisplayed()
+ confirmPageIsInCorrectPosition(5)
+
+ // Act - backward
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 0.5f * snapVelocityThreshold.toPx() },
+ delta * -1
+ )
+ }
+ rule.waitForIdle()
+
+ // Assert
+ rule.onNodeWithTag("5").assertIsDisplayed()
+ confirmPageIsInCorrectPosition(5)
+ }
+
+ @Test
+ fun swipeWithHighVelocity_defaultVelocityTreshold_shouldGoToNextPage() {
+ // Arrange
+ createPager(
+ initialPage = 5,
+
+ modifier = Modifier.fillMaxSize()
+ )
// make sure the scroll distance is not enough to go to next page
val delta = pagerSize * 0.4f * scrollForwardSign
@@ -108,10 +152,53 @@
}
@Test
- fun swipeWithHighVelocity_overHalfPage_shouldGoToNextPage() {
+ fun swipeWithHighVelocity_customVelocityThreshold_shouldGoToNextPage() {
// Arrange
val state = PagerState(5)
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ val snapVelocityThreshold = 200.dp
+ createPager(
+ state = state,
+ modifier = Modifier.fillMaxSize(),
+ snapVelocityThreshold = snapVelocityThreshold
+ )
+ // make sure the scroll distance is not enough to go to next page
+ val delta = pagerSize * 0.4f * scrollForwardSign
+
+ // Act - forward
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
+ delta
+ )
+ }
+ rule.waitForIdle()
+
+ // Assert
+ rule.onNodeWithTag("6").assertIsDisplayed()
+ confirmPageIsInCorrectPosition(6)
+
+ // Act - backward
+ onPager().performTouchInput {
+ swipeWithVelocityAcrossMainAxis(
+ with(rule.density) { 1.1f * snapVelocityThreshold.toPx() },
+ delta * -1
+ )
+ }
+ rule.waitForIdle()
+
+ // Assert
+ rule.onNodeWithTag("5").assertIsDisplayed()
+ confirmPageIsInCorrectPosition(5)
+ }
+
+ @Test
+ fun swipeWithHighVelocity_overHalfPage_shouldGoToNextPage() {
+ // Arrange
+ createPager(
+ initialPage = 5,
+
+ modifier = Modifier.fillMaxSize()
+ )
// make sure the scroll distance is not enough to go to next page
val delta = pagerSize * 0.8f * scrollForwardSign
@@ -145,8 +232,11 @@
@Test
fun scrollWithoutVelocity_shouldSettlingInClosestPage() {
// Arrange
- val state = PagerState(5)
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(
+ initialPage = 5,
+
+ modifier = Modifier.fillMaxSize()
+ )
// This will scroll 1 whole page before flinging
val delta = pagerSize * 1.4f * scrollForwardSign
@@ -157,9 +247,9 @@
rule.waitForIdle()
// Assert
- assertThat(state.currentPage).isAtMost(7)
- rule.onNodeWithTag("${state.currentPage}").assertIsDisplayed()
- confirmPageIsInCorrectPosition(state.currentPage)
+ assertThat(pagerState.currentPage).isAtMost(7)
+ rule.onNodeWithTag("${pagerState.currentPage}").assertIsDisplayed()
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
// Act - backward
onPager().performTouchInput {
@@ -168,21 +258,20 @@
rule.waitForIdle()
// Assert
- assertThat(state.currentPage).isAtLeast(5)
- rule.onNodeWithTag("${state.currentPage}").assertIsDisplayed()
- confirmPageIsInCorrectPosition(state.currentPage)
+ assertThat(pagerState.currentPage).isAtLeast(5)
+ rule.onNodeWithTag("${pagerState.currentPage}").assertIsDisplayed()
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun scrollWithSameVelocity_shouldYieldSameResult_forward() {
// Arrange
var initialPage = 1
- val state = PagerState(initialPage)
createPager(
pageSize = { PageSize.Fixed(200.dp) },
- state = state,
- modifier = Modifier.fillMaxSize(),
+ initialPage = initialPage,
pageCount = { 100 },
+ modifier = Modifier.fillMaxSize(),
snappingPage = PagerSnapDistance.atMost(3)
)
// This will scroll 0.5 page before flinging
@@ -194,13 +283,13 @@
}
rule.waitForIdle()
- val pageDisplacement = state.currentPage - initialPage
+ val pageDisplacement = pagerState.currentPage - initialPage
// Repeat starting from different places
// reset
initialPage = 10
rule.runOnIdle {
- runBlocking { state.scrollToPage(initialPage) }
+ runBlocking { pagerState.scrollToPage(initialPage) }
}
onPager().performTouchInput {
@@ -208,11 +297,11 @@
}
rule.waitForIdle()
- assertThat(state.currentPage - initialPage).isEqualTo(pageDisplacement)
+ assertThat(pagerState.currentPage - initialPage).isEqualTo(pageDisplacement)
initialPage = 50
rule.runOnIdle {
- runBlocking { state.scrollToPage(initialPage) }
+ runBlocking { pagerState.scrollToPage(initialPage) }
}
onPager().performTouchInput {
@@ -220,19 +309,18 @@
}
rule.waitForIdle()
- assertThat(state.currentPage - initialPage).isEqualTo(pageDisplacement)
+ assertThat(pagerState.currentPage - initialPage).isEqualTo(pageDisplacement)
}
@Test
fun scrollWithSameVelocity_shouldYieldSameResult_backward() {
// Arrange
var initialPage = 90
- val state = PagerState(initialPage)
createPager(
pageSize = { PageSize.Fixed(200.dp) },
- state = state,
- modifier = Modifier.fillMaxSize(),
+ initialPage = initialPage,
pageCount = { 100 },
+ modifier = Modifier.fillMaxSize(),
snappingPage = PagerSnapDistance.atMost(3)
)
// This will scroll 0.5 page before flinging
@@ -244,13 +332,13 @@
}
rule.waitForIdle()
- val pageDisplacement = state.currentPage - initialPage
+ val pageDisplacement = pagerState.currentPage - initialPage
// Repeat starting from different places
// reset
initialPage = 70
rule.runOnIdle {
- runBlocking { state.scrollToPage(initialPage) }
+ runBlocking { pagerState.scrollToPage(initialPage) }
}
onPager().performTouchInput {
@@ -258,11 +346,11 @@
}
rule.waitForIdle()
- assertThat(state.currentPage - initialPage).isEqualTo(pageDisplacement)
+ assertThat(pagerState.currentPage - initialPage).isEqualTo(pageDisplacement)
initialPage = 30
rule.runOnIdle {
- runBlocking { state.scrollToPage(initialPage) }
+ runBlocking { pagerState.scrollToPage(initialPage) }
}
onPager().performTouchInput {
@@ -270,7 +358,7 @@
}
rule.waitForIdle()
- assertThat(state.currentPage - initialPage).isEqualTo(pageDisplacement)
+ assertThat(pagerState.currentPage - initialPage).isEqualTo(pageDisplacement)
}
companion object {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
index 6e5d7ee..6b97a8e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerStateTest.kt
@@ -26,6 +26,8 @@
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.onNodeWithTag
@@ -51,7 +53,7 @@
@Test
fun pagerStateNotAttached_shouldReturnDefaultValues_andChangeAfterAttached() = runBlocking {
// Arrange
- val state = PagerState(initialPage = 5, initialPageOffsetFraction = 0.2f)
+ val state = PagerStateImpl(5, 0.2f) { DefaultPageCount }
assertThat(state.currentPage).isEqualTo(5)
assertThat(state.currentPageOffsetFraction).isEqualTo(0.2f)
@@ -59,7 +61,21 @@
val currentPage = derivedStateOf { state.currentPage }
val currentPageOffsetFraction = derivedStateOf { state.currentPageOffsetFraction }
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ rule.setContent {
+ HorizontalOrVerticalPager(
+ state = state,
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(PagerTestTag)
+ .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
+ pageSize = PageSize.Fill,
+ reverseLayout = config.reverseLayout,
+ pageSpacing = config.pageSpacing,
+ contentPadding = config.mainAxisContentPadding,
+ ) {
+ Page(index = it)
+ }
+ }
withContext(Dispatchers.Main + AutoTestFrameClock()) {
state.scrollToPage(state.currentPage + 1)
@@ -74,16 +90,15 @@
@Test
fun scrollToPage_shouldPlacePagesCorrectly() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(modifier = Modifier.fillMaxSize())
// Act and Assert
repeat(DefaultAnimationRepetition) {
- assertThat(state.currentPage).isEqualTo(it)
+ assertThat(pagerState.currentPage).isEqualTo(it)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.scrollToPage(state.currentPage + 1)
+ pagerState.scrollToPage(pagerState.currentPage + 1)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
}
@@ -91,314 +106,312 @@
@Test
fun scrollToPage_usedOffset_shouldPlacePagesCorrectly() = runBlocking {
// Arrange
- val state = PagerState()
+
suspend fun scrollToPageWithOffset(page: Int, offset: Float) {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.scrollToPage(page, offset)
+ pagerState.scrollToPage(page, offset)
}
}
// Arrange
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(modifier = Modifier.fillMaxSize())
// Act
scrollToPageWithOffset(10, 0.5f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 10, pageOffset = 0.5f)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 10, pageOffset = 0.5f)
// Act
scrollToPageWithOffset(4, 0.2f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 4, pageOffset = 0.2f)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 4, pageOffset = 0.2f)
// Act
scrollToPageWithOffset(12, -0.4f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 12, pageOffset = -0.4f)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 12, pageOffset = -0.4f)
// Act
scrollToPageWithOffset(DefaultPageCount - 1, 0.5f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, DefaultPageCount - 1)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, DefaultPageCount - 1)
// Act
scrollToPageWithOffset(0, -0.5f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 0)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 0)
}
@Test
fun scrollToPage_longSkipShouldNotPlaceIntermediatePages() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+
+ createPager(modifier = Modifier.fillMaxSize())
// Act
- assertThat(state.currentPage).isEqualTo(0)
+ assertThat(pagerState.currentPage).isEqualTo(0)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.scrollToPage(DefaultPageCount - 1)
+ pagerState.scrollToPage(DefaultPageCount - 1)
}
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(DefaultPageCount - 1)
+ assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
assertThat(placed).doesNotContain(DefaultPageCount / 2)
assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun animateScrollToPage_shouldPlacePagesCorrectly() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+
+ createPager(modifier = Modifier.fillMaxSize())
// Act and Assert
repeat(DefaultAnimationRepetition) {
- assertThat(state.currentPage).isEqualTo(it)
+ assertThat(pagerState.currentPage).isEqualTo(it)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(state.currentPage + 1)
+ pagerState.animateScrollToPage(pagerState.currentPage + 1)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
}
@Test
fun animateScrollToPage_usedOffset_shouldPlacePagesCorrectly() = runBlocking {
// Arrange
- val state = PagerState()
+
suspend fun animateScrollToPageWithOffset(page: Int, offset: Float) {
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(page, offset)
+ pagerState.animateScrollToPage(page, offset)
}
}
// Arrange
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(modifier = Modifier.fillMaxSize())
// Act
animateScrollToPageWithOffset(10, 0.5f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 10, pageOffset = 0.5f)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 10, pageOffset = 0.5f)
// Act
animateScrollToPageWithOffset(4, 0.2f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 4, pageOffset = 0.2f)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 4, pageOffset = 0.2f)
// Act
animateScrollToPageWithOffset(12, -0.4f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 12, pageOffset = -0.4f)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 12, pageOffset = -0.4f)
// Act
animateScrollToPageWithOffset(DefaultPageCount - 1, 0.5f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, DefaultPageCount - 1)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, DefaultPageCount - 1)
// Act
animateScrollToPageWithOffset(0, -0.5f)
// Assert
- confirmPageIsInCorrectPosition(state.currentPage, 0)
+ confirmPageIsInCorrectPosition(pagerState.currentPage, 0)
}
@Test
fun animateScrollToPage_longSkipShouldNotPlaceIntermediatePages() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+
+ createPager(modifier = Modifier.fillMaxSize())
// Act
- assertThat(state.currentPage).isEqualTo(0)
+ assertThat(pagerState.currentPage).isEqualTo(0)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(DefaultPageCount - 1)
+ pagerState.animateScrollToPage(DefaultPageCount - 1)
}
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(DefaultPageCount - 1)
+ assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
assertThat(placed).doesNotContain(DefaultPageCount / 2)
assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun scrollToPage_shouldCoerceWithinRange() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+
+ createPager(modifier = Modifier.fillMaxSize())
// Act
- assertThat(state.currentPage).isEqualTo(0)
+ assertThat(pagerState.currentPage).isEqualTo(0)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.scrollToPage(DefaultPageCount)
+ pagerState.scrollToPage(DefaultPageCount)
}
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(DefaultPageCount - 1) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1) }
// Act
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.scrollToPage(-1)
+ pagerState.scrollToPage(-1)
}
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(0) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(0) }
}
@Test
fun scrollToPage_usingLaunchedEffect() {
- val state = PagerState()
- createPager(state, additionalContent = {
- LaunchedEffect(state) {
- state.scrollToPage(10)
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.scrollToPage(10)
}
})
rule.waitForIdle()
- assertThat(state.currentPage).isEqualTo(10)
+ assertThat(pagerState.currentPage).isEqualTo(10)
confirmPageIsInCorrectPosition(10)
}
@Test
fun scrollToPageWithOffset_usingLaunchedEffect() {
- val state = PagerState()
- createPager(state, additionalContent = {
- LaunchedEffect(state) {
- state.scrollToPage(10, 0.4f)
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.scrollToPage(10, 0.4f)
}
})
rule.waitForIdle()
- assertThat(state.currentPage).isEqualTo(10)
+ assertThat(pagerState.currentPage).isEqualTo(10)
confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
}
@Test
fun animateScrollToPage_shouldCoerceWithinRange() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+
+ createPager(modifier = Modifier.fillMaxSize())
// Act
- assertThat(state.currentPage).isEqualTo(0)
+ assertThat(pagerState.currentPage).isEqualTo(0)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(DefaultPageCount)
+ pagerState.animateScrollToPage(DefaultPageCount)
}
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(DefaultPageCount - 1) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1) }
// Act
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(-1)
+ pagerState.animateScrollToPage(-1)
}
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(0) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(0) }
}
@Test
fun animateScrollToPage_moveToSamePageWithOffset_shouldScroll() = runBlocking {
// Arrange
- val state = PagerState(initialPage = 5)
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
// Act
- assertThat(state.currentPage).isEqualTo(5)
+ assertThat(pagerState.currentPage).isEqualTo(5)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(5, 0.4f)
+ pagerState.animateScrollToPage(5, 0.4f)
}
// Assert
- rule.runOnIdle { assertThat(state.currentPage).isEqualTo(5) }
- rule.runOnIdle { assertThat(state.currentPageOffsetFraction).isWithin(0.01f).of(0.4f) }
+ rule.runOnIdle { assertThat(pagerState.currentPage).isEqualTo(5) }
+ rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isWithin(0.01f).of(0.4f) }
}
@Test
fun animateScrollToPage_withPassedAnimation() = runBlocking {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+
+ createPager(modifier = Modifier.fillMaxSize())
val differentAnimation: AnimationSpec<Float> = tween()
// Act and Assert
repeat(DefaultAnimationRepetition) {
- assertThat(state.currentPage).isEqualTo(it)
+ assertThat(pagerState.currentPage).isEqualTo(it)
withContext(Dispatchers.Main + AutoTestFrameClock()) {
- state.animateScrollToPage(
- state.currentPage + 1,
+ pagerState.animateScrollToPage(
+ pagerState.currentPage + 1,
animationSpec = differentAnimation
)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
}
@Test
fun animatedScrollToPage_usingLaunchedEffect() {
- val state = PagerState()
- createPager(state, additionalContent = {
- LaunchedEffect(state) {
- state.animateScrollToPage(10)
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.animateScrollToPage(10)
}
})
rule.waitForIdle()
- assertThat(state.currentPage).isEqualTo(10)
+ assertThat(pagerState.currentPage).isEqualTo(10)
confirmPageIsInCorrectPosition(10)
}
@Test
fun animatedScrollToPageWithOffset_usingLaunchedEffect() {
- val state = PagerState()
- createPager(state, additionalContent = {
- LaunchedEffect(state) {
- state.animateScrollToPage(10, 0.4f)
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.animateScrollToPage(10, 0.4f)
}
})
rule.waitForIdle()
- assertThat(state.currentPage).isEqualTo(10)
+ assertThat(pagerState.currentPage).isEqualTo(10)
confirmPageIsInCorrectPosition(10, pageOffset = 0.4f)
}
@Test
fun animatedScrollToPage_viewPortNumberOfPages_usingLaunchedEffect_shouldNotPlaceALlPages() {
- val state = PagerState()
- createPager(state, additionalContent = {
- LaunchedEffect(state) {
- state.animateScrollToPage(DefaultPageCount - 1)
+
+ createPager(additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.animateScrollToPage(DefaultPageCount - 1)
}
})
rule.waitForIdle()
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(DefaultPageCount - 1)
+ assertThat(pagerState.currentPage).isEqualTo(DefaultPageCount - 1)
assertThat(placed).doesNotContain(DefaultPageCount / 2 - 1)
assertThat(placed).doesNotContain(DefaultPageCount / 2)
assertThat(placed).doesNotContain(DefaultPageCount / 2 + 1)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun currentPage_shouldChangeWhenClosestPageToSnappedPositionChanges() {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
- var previousCurrentPage = state.currentPage
+
+ createPager(modifier = Modifier.fillMaxSize())
+ var previousCurrentPage = pagerState.currentPage
// Act
// Move less than half an item
@@ -414,15 +427,15 @@
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(previousCurrentPage)
+ assertThat(pagerState.currentPage).isEqualTo(previousCurrentPage)
}
// Release pointer
onPager().performTouchInput { up() }
rule.runOnIdle {
- previousCurrentPage = state.currentPage
+ previousCurrentPage = pagerState.currentPage
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
// Arrange
// Pass closest to snap position threshold (over half an item)
@@ -440,24 +453,22 @@
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(previousCurrentPage + 1)
+ assertThat(pagerState.currentPage).isEqualTo(previousCurrentPage + 1)
}
onPager().performTouchInput { up() }
rule.waitForIdle()
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun targetPage_performScrollBelowThreshold_shouldNotShowNextPage() {
// Arrange
- val state = PagerState()
createPager(
- state = state,
modifier = Modifier.fillMaxSize(),
snappingPage = PagerSnapDistance.atMost(3)
)
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.mainClock.autoAdvance = false
// Act
@@ -465,7 +476,7 @@
val forwardDelta =
scrollForwardSign.toFloat() * with(rule.density) { DefaultPositionThreshold.toPx() / 2 }
- var previousTargetPage = state.targetPage
+ var previousTargetPage = pagerState.targetPage
onPager().performTouchInput {
down(layoutStart)
@@ -473,12 +484,12 @@
}
// Assert
- assertThat(state.targetPage).isEqualTo(previousTargetPage)
+ assertThat(pagerState.targetPage).isEqualTo(previousTargetPage)
// Reset
rule.mainClock.autoAdvance = true
onPager().performTouchInput { up() }
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
// Act
// Moving more than threshold
@@ -486,7 +497,7 @@
-DefaultPositionThreshold.toPx() / 2
}
- previousTargetPage = state.targetPage
+ previousTargetPage = pagerState.targetPage
onPager().performTouchInput {
down(layoutStart)
@@ -494,19 +505,17 @@
}
// Assert
- assertThat(state.targetPage).isEqualTo(previousTargetPage)
+ assertThat(pagerState.targetPage).isEqualTo(previousTargetPage)
}
@Test
fun targetPage_performScroll_shouldShowNextPage() {
// Arrange
- val state = PagerState()
createPager(
- state = state,
modifier = Modifier.fillMaxSize(),
snappingPage = PagerSnapDistance.atMost(3)
)
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.mainClock.autoAdvance = false
// Act
@@ -518,15 +527,15 @@
}
// Assert
- assertThat(state.targetPage).isEqualTo(state.currentPage + 1)
- assertThat(state.targetPage).isNotEqualTo(state.currentPage)
+ assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage + 1)
+ assertThat(pagerState.targetPage).isNotEqualTo(pagerState.currentPage)
// Reset
rule.mainClock.autoAdvance = true
onPager().performTouchInput { up() }
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.runOnIdle {
- runBlocking { state.scrollToPage(5) }
+ runBlocking { pagerState.scrollToPage(5) }
}
rule.mainClock.autoAdvance = false
@@ -539,117 +548,113 @@
}
// Assert
- assertThat(state.targetPage).isEqualTo(state.currentPage - 1)
- assertThat(state.targetPage).isNotEqualTo(state.currentPage)
+ assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage - 1)
+ assertThat(pagerState.targetPage).isNotEqualTo(pagerState.currentPage)
rule.mainClock.autoAdvance = true
onPager().performTouchInput { up() }
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
}
@Test
fun targetPage_performingFling_shouldGoToPredictedPage() {
// Arrange
- val state = PagerState()
+
createPager(
- state = state,
modifier = Modifier.fillMaxSize(),
snappingPage = PagerSnapDistance.atMost(3)
)
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.mainClock.autoAdvance = false
// Act
// Moving forward
- var previousTarget = state.targetPage
+ var previousTarget = pagerState.targetPage
val forwardDelta = pagerSize * scrollForwardSign.toFloat()
onPager().performTouchInput {
swipeWithVelocityAcrossMainAxis(20000f, forwardDelta)
}
- rule.mainClock.advanceTimeUntil { state.targetPage != previousTarget }
- var flingOriginIndex = state.firstVisiblePageInfo?.index ?: 0
+ rule.mainClock.advanceTimeUntil { pagerState.targetPage != previousTarget }
+ var flingOriginIndex = pagerState.firstVisiblePage
// Assert
- assertThat(state.targetPage).isEqualTo(flingOriginIndex + 3)
- assertThat(state.targetPage).isNotEqualTo(state.currentPage)
+ assertThat(pagerState.targetPage).isEqualTo(flingOriginIndex + 3)
+ assertThat(pagerState.targetPage).isNotEqualTo(pagerState.currentPage)
rule.mainClock.autoAdvance = true
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.mainClock.autoAdvance = false
// Act
// Moving backward
- previousTarget = state.targetPage
+ previousTarget = pagerState.targetPage
val backwardDelta = -pagerSize * scrollForwardSign.toFloat()
onPager().performTouchInput {
swipeWithVelocityAcrossMainAxis(20000f, backwardDelta)
}
- rule.mainClock.advanceTimeUntil { state.targetPage != previousTarget }
+ rule.mainClock.advanceTimeUntil { pagerState.targetPage != previousTarget }
// Assert
- flingOriginIndex = (state.firstVisiblePageInfo?.index ?: 0) + 1
- assertThat(state.targetPage).isEqualTo(flingOriginIndex - 3)
- assertThat(state.targetPage).isNotEqualTo(state.currentPage)
+ flingOriginIndex = pagerState.firstVisiblePage + 1
+ assertThat(pagerState.targetPage).isEqualTo(flingOriginIndex - 3)
+ assertThat(pagerState.targetPage).isNotEqualTo(pagerState.currentPage)
rule.mainClock.autoAdvance = true
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
}
@Test
fun targetPage_shouldReflectTargetWithAnimation() {
// Arrange
- val state = PagerState()
+
createPager(
- state = state,
modifier = Modifier.fillMaxSize()
)
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.mainClock.autoAdvance = false
// Act
// Moving forward
- var previousTarget = state.targetPage
+ var previousTarget = pagerState.targetPage
rule.runOnIdle {
scope.launch {
- state.animateScrollToPage(DefaultPageCount - 1)
+ pagerState.animateScrollToPage(DefaultPageCount - 1)
}
}
- rule.mainClock.advanceTimeUntil { state.targetPage != previousTarget }
+ rule.mainClock.advanceTimeUntil { pagerState.targetPage != previousTarget }
// Assert
- assertThat(state.targetPage).isEqualTo(DefaultPageCount - 1)
- assertThat(state.targetPage).isNotEqualTo(state.currentPage)
+ assertThat(pagerState.targetPage).isEqualTo(DefaultPageCount - 1)
+ assertThat(pagerState.targetPage).isNotEqualTo(pagerState.currentPage)
rule.mainClock.autoAdvance = true
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
rule.mainClock.autoAdvance = false
// Act
// Moving backward
- previousTarget = state.targetPage
+ previousTarget = pagerState.targetPage
rule.runOnIdle {
scope.launch {
- state.animateScrollToPage(0)
+ pagerState.animateScrollToPage(0)
}
}
- rule.mainClock.advanceTimeUntil { state.targetPage != previousTarget }
+ rule.mainClock.advanceTimeUntil { pagerState.targetPage != previousTarget }
// Assert
- assertThat(state.targetPage).isEqualTo(0)
- assertThat(state.targetPage).isNotEqualTo(state.currentPage)
+ assertThat(pagerState.targetPage).isEqualTo(0)
+ assertThat(pagerState.targetPage).isNotEqualTo(pagerState.currentPage)
rule.mainClock.autoAdvance = true
- rule.runOnIdle { assertThat(state.targetPage).isEqualTo(state.currentPage) }
+ rule.runOnIdle { assertThat(pagerState.targetPage).isEqualTo(pagerState.currentPage) }
}
@Test
fun settledPage_onAnimationScroll_shouldChangeOnScrollFinishedOnly() {
// Arrange
- val state = PagerState()
var settledPageChanges = 0
createPager(
- state = state,
modifier = Modifier.fillMaxSize(),
additionalContent = {
- LaunchedEffect(key1 = state.settledPage) {
+ LaunchedEffect(key1 = pagerState.settledPage) {
settledPageChanges++
}
}
@@ -657,51 +662,49 @@
// Settle page changed once for first composition
rule.runOnIdle {
- assertThat(state.settledPage).isEqualTo(state.currentPage)
+ assertThat(pagerState.settledPage).isEqualTo(pagerState.currentPage)
assertTrue { settledPageChanges == 1 }
}
settledPageChanges = 0
- val previousSettled = state.settledPage
+ val previousSettled = pagerState.settledPage
rule.mainClock.autoAdvance = false
// Act
// Moving forward
rule.runOnIdle {
scope.launch {
- state.animateScrollToPage(DefaultPageCount - 1)
+ pagerState.animateScrollToPage(DefaultPageCount - 1)
}
}
// Settled page shouldn't change whilst scroll is in progress.
- assertTrue { state.isScrollInProgress }
+ assertTrue { pagerState.isScrollInProgress }
assertTrue { settledPageChanges == 0 }
- assertThat(state.settledPage).isEqualTo(previousSettled)
+ assertThat(pagerState.settledPage).isEqualTo(previousSettled)
rule.mainClock.advanceTimeUntil { settledPageChanges != 0 }
rule.runOnIdle {
- assertTrue { !state.isScrollInProgress }
- assertThat(state.settledPage).isEqualTo(state.currentPage)
+ assertTrue { !pagerState.isScrollInProgress }
+ assertThat(pagerState.settledPage).isEqualTo(pagerState.currentPage)
}
}
@Test
fun settledPage_onGestureScroll_shouldChangeOnScrollFinishedOnly() {
// Arrange
- val state = PagerState()
var settledPageChanges = 0
createPager(
- state = state,
modifier = Modifier.fillMaxSize(),
additionalContent = {
- LaunchedEffect(key1 = state.settledPage) {
+ LaunchedEffect(key1 = pagerState.settledPage) {
settledPageChanges++
}
}
)
settledPageChanges = 0
- val previousSettled = state.settledPage
+ val previousSettled = pagerState.settledPage
rule.mainClock.autoAdvance = false
// Act
// Moving forward
@@ -711,27 +714,26 @@
}
// Settled page shouldn't change whilst scroll is in progress.
- assertTrue { state.isScrollInProgress }
+ assertTrue { pagerState.isScrollInProgress }
assertTrue { settledPageChanges == 0 }
- assertThat(state.settledPage).isEqualTo(previousSettled)
+ assertThat(pagerState.settledPage).isEqualTo(previousSettled)
rule.mainClock.advanceTimeUntil { settledPageChanges != 0 }
rule.runOnIdle {
- assertTrue { !state.isScrollInProgress }
- assertThat(state.settledPage).isEqualTo(state.currentPage)
+ assertTrue { !pagerState.isScrollInProgress }
+ assertThat(pagerState.settledPage).isEqualTo(pagerState.currentPage)
}
}
@Test
fun currentPageOffset_shouldReflectScrollingOfCurrentPage() {
// Arrange
- val state = PagerState(DefaultPageCount / 2)
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(initialPage = DefaultPageCount / 2, modifier = Modifier.fillMaxSize())
// No offset initially
rule.runOnIdle {
- assertThat(state.currentPageOffsetFraction).isWithin(0.01f).of(0f)
+ assertThat(pagerState.currentPageOffsetFraction).isWithin(0.01f).of(0f)
}
// Act
@@ -746,7 +748,7 @@
}
rule.runOnIdle {
- assertThat(state.currentPageOffsetFraction).isWithin(0.1f).of(0.25f)
+ assertThat(pagerState.currentPageOffsetFraction).isWithin(0.1f).of(0.25f)
}
onPager().performTouchInput { up() }
@@ -755,13 +757,13 @@
// Reset
rule.runOnIdle {
scope.launch {
- state.scrollToPage(DefaultPageCount / 2)
+ pagerState.scrollToPage(DefaultPageCount / 2)
}
}
// No offset initially (again)
rule.runOnIdle {
- assertThat(state.currentPageOffsetFraction).isWithin(0.01f).of(0f)
+ assertThat(pagerState.currentPageOffsetFraction).isWithin(0.01f).of(0f)
}
// Act
@@ -776,23 +778,22 @@
}
rule.runOnIdle {
- assertThat(state.currentPageOffsetFraction).isWithin(0.1f).of(-0.25f)
+ assertThat(pagerState.currentPageOffsetFraction).isWithin(0.1f).of(-0.25f)
}
}
@Test
fun initialPageOnPagerState_shouldDisplayThatPageFirst() {
// Arrange
- val state = PagerState(5)
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(initialPage = 5, modifier = Modifier.fillMaxSize())
// Assert
rule.onNodeWithTag("4").assertDoesNotExist()
rule.onNodeWithTag("5").assertIsDisplayed()
rule.onNodeWithTag("6").assertDoesNotExist()
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
@@ -801,11 +802,10 @@
val tester = StateRestorationTester(rule)
lateinit var state: PagerState
tester.setContent {
- state = rememberPagerState()
+ state = rememberPagerState(pageCount = { DefaultPageCount })
scope = rememberCoroutineScope()
HorizontalOrVerticalPager(
state = state,
- pageCount = DefaultPageCount,
modifier = Modifier.fillMaxSize()
) {
Page(it)
@@ -838,27 +838,27 @@
@Test
fun scrollTo_beforeFirstLayout_shouldWaitForStateAndLayoutSetting() {
// Arrange
- val state = PagerState(0)
+
rule.mainClock.autoAdvance = false
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize(), additionalContent = {
- LaunchedEffect(state) {
- state.scrollToPage(5)
+ createPager(modifier = Modifier.fillMaxSize(), additionalContent = {
+ LaunchedEffect(pagerState) {
+ pagerState.scrollToPage(5)
}
})
// Assert
- assertThat(state.currentPage).isEqualTo(5)
+ assertThat(pagerState.currentPage).isEqualTo(5)
}
@Test
fun currentPageOffsetFraction_shouldNeverBeNan() {
rule.setContent {
- val state = rememberPagerState()
+ val state = rememberPagerState(pageCount = { 10 })
// Read state in composition, should never be Nan
assertFalse { state.currentPageOffsetFraction.isNaN() }
- HorizontalOrVerticalPager(pageCount = 10, state = state) {
+ HorizontalOrVerticalPager(state = state) {
Page(index = it)
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt
index 7a5a1bc..8926c1f 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerSwipeEdgeTest.kt
@@ -38,8 +38,7 @@
@Test
fun swipePageTowardsEdge_shouldNotMove() {
// Arrange
- val state = PagerState()
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(modifier = Modifier.fillMaxSize())
val delta = pagerSize * 0.4f * scrollForwardSign
// Act - backward
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
index 24b1450..753c0a2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
@@ -20,12 +20,16 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTouchInput
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,9 +48,8 @@
@Test
fun userScrollEnabledIsOff_shouldNotAllowGestureScroll() {
// Arrange
- val state = PagerState()
+
createPager(
- state = state,
userScrollEnabled = false,
modifier = Modifier.fillMaxSize()
)
@@ -56,7 +59,7 @@
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(0)
+ assertThat(pagerState.currentPage).isEqualTo(0)
}
confirmPageIsInCorrectPosition(0, 0)
@@ -65,9 +68,8 @@
@Test
fun userScrollEnabledIsOff_shouldAllowAnimationScroll() {
// Arrange
- val state = PagerState()
+
createPager(
- state = state,
userScrollEnabled = false,
modifier = Modifier.fillMaxSize()
)
@@ -75,13 +77,13 @@
// Act
rule.runOnIdle {
scope.launch {
- state.animateScrollToPage(5)
+ pagerState.animateScrollToPage(5)
}
}
// Assert
rule.runOnIdle {
- assertThat(state.currentPage).isEqualTo(5)
+ assertThat(pagerState.currentPage).isEqualTo(5)
}
confirmPageIsInCorrectPosition(5)
}
@@ -89,9 +91,8 @@
@Test
fun userScrollEnabledIsOn_shouldAllowGestureScroll() {
// Arrange
- val state = PagerState(5)
createPager(
- state = state,
+ initialPage = 5,
userScrollEnabled = true,
modifier = Modifier.fillMaxSize()
)
@@ -99,25 +100,24 @@
onPager().performTouchInput { swipeWithVelocityAcrossMainAxis(1000f) }
rule.runOnIdle {
- assertThat(state.currentPage).isNotEqualTo(5)
+ assertThat(pagerState.currentPage).isNotEqualTo(5)
}
- confirmPageIsInCorrectPosition(state.currentPage)
+ confirmPageIsInCorrectPosition(pagerState.currentPage)
}
@Test
fun pageCount_pagerOnlyContainsGivenPageCountItems() {
// Arrange
- val state = PagerState()
// Act
- createPager(state = state, modifier = Modifier.fillMaxSize())
+ createPager(modifier = Modifier.fillMaxSize())
// Assert
repeat(DefaultPageCount) {
rule.onNodeWithTag("$it").assertIsDisplayed()
rule.runOnIdle {
scope.launch {
- state.scroll {
+ pagerState.scroll {
scrollBy(pagerSize.toFloat())
}
}
@@ -130,12 +130,10 @@
@Test
fun mutablePageCount_assertPagesAreChangedIfCountIsChanged() {
// Arrange
- val state = PagerState()
val pageCount = mutableStateOf(2)
createPager(
- state = state,
+ pageCount = { pageCount.value },
modifier = Modifier.fillMaxSize(),
- pageCount = { pageCount.value }
)
rule.onNodeWithTag("3").assertDoesNotExist()
@@ -149,7 +147,7 @@
rule.onNodeWithTag("$it").assertIsDisplayed()
rule.runOnIdle {
scope.launch {
- state.scroll {
+ pagerState.scroll {
scrollBy(pagerSize.toFloat())
}
}
@@ -158,6 +156,100 @@
}
}
+ @Test
+ fun pageCount_readBeforeCompositionIsAccurate() {
+ // Arrange
+ val pageCount = mutableStateOf(2)
+ val state = PagerStateImpl(0, 0f) { pageCount.value }
+ assertThat(state.pageCount).isEqualTo(pageCount.value)
+ rule.setContent {
+ HorizontalOrVerticalPager(
+ state = state,
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(PagerTestTag)
+ .onSizeChanged { pagerSize = if (vertical) it.height else it.width },
+ pageSize = PageSize.Fill,
+ reverseLayout = config.reverseLayout,
+ pageSpacing = config.pageSpacing,
+ contentPadding = config.mainAxisContentPadding,
+ ) {
+ Page(index = it)
+ }
+ }
+
+ rule.runOnIdle { pageCount.value = 5 }
+ assertThat(state.pageCount).isEqualTo(pageCount.value)
+ }
+
+ @Test
+ fun pageCount_changeInCountDoesNotCausePagerToRecompose() {
+ // Arrange
+ var recomposeCount = 0
+ val pageCount = mutableStateOf(2)
+ val state = PagerStateImpl(0, 0f) { pageCount.value }
+ assertThat(state.pageCount).isEqualTo(pageCount.value)
+
+ rule.setContent {
+ HorizontalOrVerticalPager(
+ state = state,
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(PagerTestTag)
+ .composed {
+ recomposeCount++
+ Modifier
+ },
+ pageSize = PageSize.Fill,
+ reverseLayout = config.reverseLayout,
+ pageSpacing = config.pageSpacing,
+ contentPadding = config.mainAxisContentPadding,
+ ) {
+ Page(index = it)
+ }
+ }
+
+ assertThat(recomposeCount).isEqualTo(1)
+ rule.runOnIdle { pageCount.value = 5 } // change count
+ assertThat(state.pageCount).isEqualTo(pageCount.value)
+ assertThat(recomposeCount).isEqualTo(1)
+ }
+
+ @Test
+ fun pageCountDecreased_currentPageIsAdjustedAccordingly() {
+ // Arrange
+ val pageCount = mutableStateOf(5)
+ val state = PagerStateImpl(0, 0f) { pageCount.value }
+ assertThat(state.pageCount).isEqualTo(pageCount.value)
+
+ rule.setContent {
+ HorizontalOrVerticalPager(
+ state = state,
+ modifier = Modifier
+ .fillMaxSize()
+ .testTag(PagerTestTag),
+ pageSize = PageSize.Fill,
+ reverseLayout = config.reverseLayout,
+ pageSpacing = config.pageSpacing,
+ contentPadding = config.mainAxisContentPadding,
+ ) {
+ Page(index = it)
+ }
+ }
+
+ rule.runOnIdle {
+ runBlocking {
+ state.scrollToPage(3)
+ }
+ }
+ rule.runOnIdle { assertThat(state.currentPage).isEqualTo(3) }
+ pageCount.value = 2 // change count, less than current page
+ rule.runOnIdle {
+ assertThat(state.pageCount).isEqualTo(pageCount.value)
+ assertThat(state.currentPage).isEqualTo(1) // last page
+ }
+ }
+
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index ce3dd25..9f00444 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -37,9 +37,11 @@
import androidx.compose.foundation.lazy.layout.lazyLayoutSemantics
import androidx.compose.foundation.overscroll
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
@@ -53,8 +55,6 @@
internal fun Pager(
/** Modifier to be applied for the inner layout */
modifier: Modifier,
- /** The amount of Pages that will be present in this Pager **/
- pageCount: Int,
/** State controlling the scroll position */
state: PagerState,
/** The inner padding to be added for the whole content(not for each individual page) */
@@ -94,9 +94,8 @@
val pagerItemProvider = rememberPagerItemProvider(
state = state,
pageContent = pageContent,
- key = key,
- pageCount = pageCount
- )
+ key = key
+ ) { state.pageCount }
val beyondBoundsInfo = remember { LazyListBeyondBoundsInfo() }
@@ -111,7 +110,7 @@
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
itemProvider = pagerItemProvider,
- pageCount = pageCount,
+ pageCount = { state.pageCount },
beyondBoundsInfo = beyondBoundsInfo
)
@@ -172,10 +171,11 @@
val state: PagerState,
latestContent: () -> (@Composable (page: Int) -> Unit),
key: ((index: Int) -> Any)?,
- pageCount: Int
+ pageCount: () -> Int
) : LazyLayoutItemProvider {
- private val pagerContent =
- PagerLayoutIntervalContent(latestContent(), key = key, pageCount = pageCount)
+ private val pagerContent by derivedStateOf(structuralEqualityPolicy()) {
+ PagerLayoutIntervalContent(latestContent(), key = key, pageCount = pageCount())
+ }
private val keyToIndexMap: LazyLayoutKeyIndexMap by NearestRangeKeyIndexMapState(
firstVisibleItemIndex = { state.firstVisiblePage },
slidingWindowSize = { NearestItemsSlidingWindowSize },
@@ -203,8 +203,8 @@
val key: ((index: Int) -> Any)?,
val pageCount: Int
) : LazyLayoutIntervalContent<PagerIntervalContent>() {
- override val intervals: IntervalList<PagerIntervalContent>
- get() = MutableIntervalList<PagerIntervalContent>().apply {
+ override val intervals: IntervalList<PagerIntervalContent> =
+ MutableIntervalList<PagerIntervalContent>().apply {
addInterval(pageCount, PagerIntervalContent(key = key, item = pageContent))
}
}
@@ -221,7 +221,7 @@
state: PagerState,
pageContent: @Composable (page: Int) -> Unit,
key: ((index: Int) -> Any)?,
- pageCount: Int
+ pageCount: () -> Int
): PagerLazyLayoutItemProvider {
val latestContent = rememberUpdatedState(pageContent)
return remember(state, latestContent, key, pageCount) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
index 0c55052..c2d2fc5 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/Pager.kt
@@ -28,6 +28,7 @@
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.snapping.MinFlingVelocityDp
import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import androidx.compose.foundation.layout.PaddingValues
@@ -72,6 +73,84 @@
* Please refer to the sample to learn how to use this API.
* @sample androidx.compose.foundation.samples.SimpleHorizontalPagerSample
*
+ * @param state The state to control this pager
+ * @param modifier A modifier instance to be applied to this Pager outer layout
+ * @param contentPadding a padding around the whole content. This will add padding for the
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first page or after the last one. Use [pageSpacing] to add spacing
+ * between the pages.
+ * @param pageSize Use this to change how the pages will look like inside this pager.
+ * @param beyondBoundsPageCount Pages to load before and after the list of visible
+ * pages. Note: Be aware that using a large value for [beyondBoundsPageCount] will cause a lot of
+ * pages to be composed, measured and placed which will defeat the purpose of using lazy loading.
+ * This should be used as an optimization to pre-load a couple of pages before and after the visible
+ * ones.
+ * @param pageSpacing The amount of space to be used to separate the pages in this Pager
+ * @param verticalAlignment How pages are aligned vertically in this Pager.
+ * @param flingBehavior The [FlingBehavior] to be used for post scroll gestures.
+ * @param userScrollEnabled whether the scrolling via the user gestures or accessibility actions
+ * is allowed. You can still scroll programmatically using [PagerState.scroll] even when it is
+ * disabled.
+ * @param reverseLayout reverse the direction of scrolling and layout.
+ * @param key a stable and unique key representing the item. When you specify the key the scroll
+ * position will be maintained based on the key, which means if you add/remove items before the
+ * current visible item the item with the given key will be kept as the first visible one.
+ * @param pageNestedScrollConnection A [NestedScrollConnection] that dictates how this [Pager]
+ * behaves with nested lists. The default behavior will see [Pager] to consume all nested deltas.
+ * @param pageContent This Pager's page Composable.
+ */
+@Composable
+@ExperimentalFoundationApi
+fun HorizontalPager(
+ state: PagerState,
+ modifier: Modifier = Modifier,
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ pageSize: PageSize = PageSize.Fill,
+ beyondBoundsPageCount: Int = 0,
+ pageSpacing: Dp = 0.dp,
+ verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+ flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state),
+ userScrollEnabled: Boolean = true,
+ reverseLayout: Boolean = false,
+ key: ((index: Int) -> Any)? = null,
+ pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
+ Orientation.Horizontal
+ ),
+ pageContent: @Composable (page: Int) -> Unit
+) {
+ Pager(
+ state = state,
+ modifier = modifier,
+ contentPadding = contentPadding,
+ pageSize = pageSize,
+ beyondBoundsPageCount = beyondBoundsPageCount,
+ pageSpacing = pageSpacing,
+ orientation = Orientation.Horizontal,
+ verticalAlignment = verticalAlignment,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ flingBehavior = flingBehavior,
+ userScrollEnabled = userScrollEnabled,
+ reverseLayout = reverseLayout,
+ key = key,
+ pageNestedScrollConnection = pageNestedScrollConnection,
+ pageContent = pageContent
+ )
+}
+
+/**
+ * A Pager that scrolls horizontally. Pages are lazily placed in accordance to the available
+ * viewport size. By definition, pages in a [Pager] have the same size, defined by [pageSize] and
+ * use a snap animation (provided by [flingBehavior] to scroll pages into a specific position). You
+ * can use [beyondBoundsPageCount] to place more pages before and after the visible pages.
+ *
+ * If you need snapping with pages of different size, you can use a [SnapFlingBehavior] with a
+ * [SnapLayoutInfoProvider] adapted to a LazyList.
+ * @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
+ * of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
+ *
+ * Please refer to the sample to learn how to use this API.
+ * @sample androidx.compose.foundation.samples.SimpleHorizontalPagerSample
+ *
* @param pageCount The number of pages this Pager will contain
* @param modifier A modifier instance to be applied to this Pager outer layout
* @param state The state to control this pager
@@ -99,12 +178,38 @@
* behaves with nested lists. The default behavior will see [Pager] to consume all nested deltas.
* @param pageContent This Pager's page Composable.
*/
+@Deprecated(
+ "Please use the overload without pageCount. pageCount should be provided " +
+ "through PagerState.", ReplaceWith(
+ """HorizontalPager(
+ modifier = modifier,
+ state = state,
+ pageSpacing = pageSpacing,
+ horizontalAlignment = horizontalAlignment,
+ userScrollEnabled = userScrollEnabled,
+ reverseLayout = reverseLayout,
+ contentPadding = contentPadding,
+ beyondBoundsPageCount = beyondBoundsPageCount,
+ pageSize = pageSize,
+ flingBehavior = flingBehavior,
+ key = key,
+ pageNestedScrollConnection = pageNestedScrollConnection,
+ pageContent = pageContent
+ )""",
+ imports = arrayOf(
+ "androidx.compose.foundation.gestures.Orientation",
+ "androidx.compose.foundation.layout.PaddingValues",
+ "androidx.compose.foundation.pager.PageSize",
+ "androidx.compose.foundation.pager.PagerDefaults"
+ )
+ )
+)
@Composable
@ExperimentalFoundationApi
fun HorizontalPager(
pageCount: Int,
modifier: Modifier = Modifier,
- state: PagerState = rememberPagerState(),
+ state: PagerState = rememberPagerState { pageCount },
contentPadding: PaddingValues = PaddingValues(0.dp),
pageSize: PageSize = PageSize.Fill,
beyondBoundsPageCount: Int = 0,
@@ -120,22 +225,99 @@
pageContent: @Composable (page: Int) -> Unit
) {
Pager(
- modifier = modifier,
- pageCount = pageCount,
state = state,
+ modifier = modifier,
contentPadding = contentPadding,
- reverseLayout = reverseLayout,
- orientation = Orientation.Horizontal,
- flingBehavior = flingBehavior,
- userScrollEnabled = userScrollEnabled,
pageSize = pageSize,
beyondBoundsPageCount = beyondBoundsPageCount,
pageSpacing = pageSpacing,
- pageContent = pageContent,
- pageNestedScrollConnection = pageNestedScrollConnection,
+ orientation = Orientation.Horizontal,
verticalAlignment = verticalAlignment,
horizontalAlignment = Alignment.CenterHorizontally,
- key = key
+ flingBehavior = flingBehavior,
+ userScrollEnabled = userScrollEnabled,
+ reverseLayout = reverseLayout,
+ key = key,
+ pageNestedScrollConnection = pageNestedScrollConnection,
+ pageContent = pageContent
+ )
+}
+
+/**
+ * A Pager that scrolls vertically. Pages are lazily placed in accordance to the available
+ * viewport size. By definition, pages in a [Pager] have the same size, defined by [pageSize] and
+ * use a snap animation (provided by [flingBehavior] to scroll pages into a specific position). You
+ * can use [beyondBoundsPageCount] to place more pages before and after the visible pages.
+ *
+ * If you need snapping with pages of different size, you can use a [SnapFlingBehavior] with a
+ * [SnapLayoutInfoProvider] adapted to a LazyList.
+ * @see androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider for the implementation
+ * of a [SnapLayoutInfoProvider] that uses [androidx.compose.foundation.lazy.LazyListState].
+ *
+ * Please refer to the sample to learn how to use this API.
+ * @sample androidx.compose.foundation.samples.SimpleVerticalPagerSample
+ *
+ * @param state The state to control this pager
+ * @param modifier A modifier instance to be apply to this Pager outer layout
+ * @param contentPadding a padding around the whole content. This will add padding for the
+ * content after it has been clipped, which is not possible via [modifier] param. You can use it
+ * to add a padding before the first page or after the last one. Use [pageSpacing] to add spacing
+ * between the pages.
+ * @param pageSize Use this to change how the pages will look like inside this pager.
+ * @param beyondBoundsPageCount Pages to load before and after the list of visible
+ * pages. Note: Be aware that using a large value for [beyondBoundsPageCount] will cause a lot of
+ * pages to be composed, measured and placed which will defeat the purpose of using lazy loading.
+ * This should be used as an optimization to pre-load a couple of pages before and after the visible
+ * ones.
+ * @param pageSpacing The amount of space to be used to separate the pages in this Pager
+ * @param horizontalAlignment How pages are aligned horizontally in this Pager.
+ * @param flingBehavior The [FlingBehavior] to be used for post scroll gestures.
+ * @param userScrollEnabled whether the scrolling via the user gestures or accessibility actions
+ * is allowed. You can still scroll programmatically using [PagerState.scroll] even when it is
+ * disabled.
+ * @param reverseLayout reverse the direction of scrolling and layout.
+ * @param key a stable and unique key representing the item. When you specify the key the scroll
+ * position will be maintained based on the key, which means if you add/remove items before the
+ * current visible item the item with the given key will be kept as the first visible one.
+ * @param pageNestedScrollConnection A [NestedScrollConnection] that dictates how this [Pager] behaves
+ * with nested lists. The default behavior will see [Pager] to consume all nested deltas.
+ * @param pageContent This Pager's page Composable.
+ */
+@Composable
+@ExperimentalFoundationApi
+fun VerticalPager(
+ state: PagerState,
+ modifier: Modifier = Modifier,
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ pageSize: PageSize = PageSize.Fill,
+ beyondBoundsPageCount: Int = 0,
+ pageSpacing: Dp = 0.dp,
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ flingBehavior: SnapFlingBehavior = PagerDefaults.flingBehavior(state = state),
+ userScrollEnabled: Boolean = true,
+ reverseLayout: Boolean = false,
+ key: ((index: Int) -> Any)? = null,
+ pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
+ Orientation.Vertical
+ ),
+ pageContent: @Composable (page: Int) -> Unit
+) {
+ Pager(
+ state = state,
+ modifier = modifier,
+ contentPadding = contentPadding,
+ pageSize = pageSize,
+ beyondBoundsPageCount = beyondBoundsPageCount,
+ pageSpacing = pageSpacing,
+ orientation = Orientation.Vertical,
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalAlignment = horizontalAlignment,
+ flingBehavior = flingBehavior,
+ userScrollEnabled = userScrollEnabled,
+ reverseLayout = reverseLayout,
+ key = key,
+ pageNestedScrollConnection = pageNestedScrollConnection,
+ pageContent = pageContent
)
}
@@ -180,12 +362,38 @@
* with nested lists. The default behavior will see [Pager] to consume all nested deltas.
* @param pageContent This Pager's page Composable.
*/
+@Deprecated(
+ "Please use the overload without pageCount. pageCount should be provided " +
+ "through PagerState.", ReplaceWith(
+ """VerticalPager(
+ modifier = modifier,
+ state = state,
+ pageSpacing = pageSpacing,
+ horizontalAlignment = horizontalAlignment,
+ userScrollEnabled = userScrollEnabled,
+ reverseLayout = reverseLayout,
+ contentPadding = contentPadding,
+ beyondBoundsPageCount = beyondBoundsPageCount,
+ pageSize = pageSize,
+ flingBehavior = flingBehavior,
+ key = key,
+ pageNestedScrollConnection = pageNestedScrollConnection,
+ pageContent = pageContent
+ )""",
+ imports = arrayOf(
+ "androidx.compose.foundation.gestures.Orientation",
+ "androidx.compose.foundation.layout.PaddingValues",
+ "androidx.compose.foundation.pager.PageSize",
+ "androidx.compose.foundation.pager.PagerDefaults"
+ )
+ )
+)
@Composable
@ExperimentalFoundationApi
fun VerticalPager(
pageCount: Int,
modifier: Modifier = Modifier,
- state: PagerState = rememberPagerState(),
+ state: PagerState = rememberPagerState { pageCount },
contentPadding: PaddingValues = PaddingValues(0.dp),
pageSize: PageSize = PageSize.Fill,
beyondBoundsPageCount: Int = 0,
@@ -201,22 +409,21 @@
pageContent: @Composable (page: Int) -> Unit
) {
Pager(
- modifier = modifier,
- pageCount = pageCount,
state = state,
+ modifier = modifier,
contentPadding = contentPadding,
- reverseLayout = reverseLayout,
- orientation = Orientation.Vertical,
- flingBehavior = flingBehavior,
- userScrollEnabled = userScrollEnabled,
pageSize = pageSize,
beyondBoundsPageCount = beyondBoundsPageCount,
pageSpacing = pageSpacing,
- pageContent = pageContent,
- pageNestedScrollConnection = pageNestedScrollConnection,
+ orientation = Orientation.Vertical,
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = horizontalAlignment,
- key = key
+ flingBehavior = flingBehavior,
+ userScrollEnabled = userScrollEnabled,
+ reverseLayout = reverseLayout,
+ key = key,
+ pageNestedScrollConnection = pageNestedScrollConnection,
+ pageContent = pageContent
)
}
@@ -285,11 +492,17 @@
* @param highVelocityAnimationSpec The animation spec used to approach the target offset. When
* the fling velocity is large enough. Large enough means large enough to naturally decay.
* @param snapAnimationSpec The animation spec used to finally snap to the position.
+ * @param snapVelocityThreshold The minimum velocity required for a fling to be considered
+ * high enough to make pages animate through [lowVelocityAnimationSpec] and
+ * [highVelocityAnimationSpec].
*
* @return An instance of [FlingBehavior] that will perform Snapping to the next page by
* default. The animation will be governed by the post scroll velocity and we'll use either
* [lowVelocityAnimationSpec] or [highVelocityAnimationSpec] to approach the snapped position
- * and the last step of the animation will be performed by [snapAnimationSpec].
+ * and the last step of the animation will be performed by [snapAnimationSpec]. If a velocity
+ * is not high enough (lower than [snapVelocityThreshold]) the pager will use
+ * [snapAnimationSpec] to reach the snapped position. If the velocity is high enough, we'll
+ * use the logic described in [highVelocityAnimationSpec] and [lowVelocityAnimationSpec].
*/
@Composable
fun flingBehavior(
@@ -301,6 +514,7 @@
),
highVelocityAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow),
+ snapVelocityThreshold: Dp = MinFlingVelocityDp
): SnapFlingBehavior {
val density = LocalDensity.current
@@ -322,7 +536,8 @@
lowVelocityAnimationSpec = lowVelocityAnimationSpec,
highVelocityAnimationSpec = highVelocityAnimationSpec,
snapAnimationSpec = snapAnimationSpec,
- density = density
+ density = density,
+ shortSnapVelocityThreshold = snapVelocityThreshold
)
}
}
@@ -457,9 +672,11 @@
val effectivePageSizePx = pagerState.pageSize + pagerState.pageSpacing
val animationOffsetPx =
decayAnimationSpec.calculateTargetValue(0f, initialVelocity)
- val startPage = pagerState.firstVisiblePageInfo?.let {
- if (initialVelocity < 0) it.index + 1 else it.index
- } ?: pagerState.currentPage
+ val startPage = if (initialVelocity < 0) {
+ pagerState.firstVisiblePage + 1
+ } else {
+ pagerState.firstVisiblePage
+ }
val scrollOffset =
layoutInfo.visiblePagesInfo.fastFirstOrNull { it.index == startPage }?.offset ?: 0
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
index 78aa13a..6785dea 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -64,11 +64,8 @@
): PagerMeasureResult {
require(beforeContentPadding >= 0)
require(afterContentPadding >= 0)
-
val pageSizeWithSpacing = (pageAvailableSize + spaceBetweenPages).coerceAtLeast(0)
-
debugLog { "Remeasuring..." }
-
return if (pageCount <= 0) {
PagerMeasureResult(
visiblePagesInfo = emptyList(),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
index f535064..0c56d50 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasurePolicy.kt
@@ -50,7 +50,7 @@
pageSize: PageSize,
horizontalAlignment: Alignment.Horizontal?,
verticalAlignment: Alignment.Vertical?,
- pageCount: Int,
+ pageCount: () -> Int,
beyondBoundsInfo: LazyListBeyondBoundsInfo
) = remember<LazyLayoutMeasureScope.(Constraints) -> MeasureResult>(
contentPadding,
@@ -156,7 +156,7 @@
beforeContentPadding = beforeContentPadding,
afterContentPadding = afterContentPadding,
constraints = contentConstraints,
- pageCount = pageCount,
+ pageCount = pageCount(),
spaceBetweenPages = spaceBetweenPages,
mainAxisAvailableSize = mainAxisAvailableSize,
visualPageOffset = visualItemOffset,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index 04e021f..3906300 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -61,15 +61,92 @@
* @param initialPageOffsetFraction The offset of the initial page as a fraction of the page size.
* This should vary between -0.5 and 0.5 and indicates how to offset the initial page from the
* snapped position.
+ * @param pageCount The amount of pages this Pager will have.
*/
@ExperimentalFoundationApi
@Composable
fun rememberPagerState(
initialPage: Int = 0,
+ initialPageOffsetFraction: Float = 0f,
+ pageCount: () -> Int
+): PagerState {
+ return rememberSaveable(saver = PagerStateImpl.Saver) {
+ PagerStateImpl(
+ initialPage,
+ initialPageOffsetFraction,
+ pageCount
+ )
+ }.apply {
+ pageCountState.value = pageCount
+ }
+}
+
+/**
+ * Creates and remember a [PagerState] to be used with a [Pager]
+ *
+ * Please refer to the sample to learn how to use this API.
+ * @sample androidx.compose.foundation.samples.PagerWithStateSample
+ *
+ * @param initialPage The pager that should be shown first.
+ * @param initialPageOffsetFraction The offset of the initial page as a fraction of the page size.
+ * This should vary between -0.5 and 0.5 and indicates how to offset the initial page from the
+ * snapped position.
+ */
+@Deprecated(
+ "Please use the overload where you can provide a source of truth for the pageCount.",
+ ReplaceWith(
+ """rememberPagerState(
+ initialPage = initialPage,
+ initialPageOffsetFraction = initialPageOffsetFraction
+ ){
+ // provide pageCount
+ }"""
+ )
+)
+@ExperimentalFoundationApi
+@Composable
+fun rememberPagerState(
+ initialPage: Int = 0,
initialPageOffsetFraction: Float = 0f
): PagerState {
- return rememberSaveable(saver = PagerState.Saver) {
- PagerState(initialPage = initialPage, initialPageOffsetFraction = initialPageOffsetFraction)
+ return rememberSaveable(saver = PagerStateImpl.Saver) {
+ PagerStateImpl(
+ initialPage = initialPage,
+ initialPageOffsetFraction = initialPageOffsetFraction
+ ) { 0 }
+ }
+}
+
+@ExperimentalFoundationApi
+internal class PagerStateImpl(
+ initialPage: Int,
+ initialPageOffsetFraction: Float,
+ updatedPageCount: () -> Int
+) : PagerState(initialPage, initialPageOffsetFraction) {
+
+ var pageCountState = mutableStateOf(updatedPageCount)
+ override val pageCount: Int get() = pageCountState.value.invoke()
+
+ companion object {
+ /**
+ * To keep current page and current page offset saved
+ */
+ val Saver: Saver<PagerStateImpl, *> = listSaver(
+ save = {
+ listOf(
+ it.currentPage,
+ it.currentPageOffsetFraction,
+ it.pageCount
+ )
+ },
+ restore = {
+ PagerStateImpl(
+ initialPage = it[0] as Int,
+ initialPageOffsetFraction = it[1] as Float,
+ updatedPageCount = { it[2] as Int }
+ )
+ }
+ )
}
}
@@ -81,11 +158,16 @@
*/
@ExperimentalFoundationApi
@Stable
-class PagerState(
+abstract class PagerState(
val initialPage: Int = 0,
val initialPageOffsetFraction: Float = 0f
) : ScrollableState {
+ /**
+ * The total amount of pages present in this pager
+ */
+ abstract val pageCount: Int
+
init {
require(initialPageOffsetFraction in -0.5..0.5) {
"initialPageOffsetFraction $initialPageOffsetFraction is " +
@@ -166,18 +248,6 @@
minThreshold / pageSize.toFloat()
}
- internal val pageCount: Int
- get() = pagerLayoutInfoState.value.pagesCount
-
- internal val firstVisiblePageInfo: PageInfo?
- get() = visiblePages.lastOrNull {
- density.calculateDistanceToDesiredSnapPosition(
- pagerLayoutInfoState.value,
- it,
- SnapAlignmentStartToStart
- ) <= 0
- }
-
private val distanceToSnapPosition: Float
get() = layoutInfo.closestPageToSnapPosition?.let {
density.calculateDistanceToDesiredSnapPosition(
@@ -422,9 +492,9 @@
override val isScrollInProgress: Boolean
get() = scrollableState.isScrollInProgress
- override var canScrollForward: Boolean by mutableStateOf(false)
+ final override var canScrollForward: Boolean by mutableStateOf(false)
private set
- override var canScrollBackward: Boolean by mutableStateOf(false)
+ final override var canScrollBackward: Boolean by mutableStateOf(false)
private set
/**
@@ -529,26 +599,6 @@
}
}
}
-
- companion object {
- /**
- * To keep current page and current page offset saved
- */
- val Saver: Saver<PagerState, *> = listSaver(
- save = {
- listOf(
- it.currentPage,
- it.currentPageOffsetFraction
- )
- },
- restore = {
- PagerState(
- initialPage = it[0] as Int,
- initialPageOffsetFraction = it[1] as Float
- )
- }
- )
- }
}
@OptIn(ExperimentalFoundationApi::class)
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerActivity.kt
index a9466ca..c7e90d4 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerActivity.kt
@@ -44,7 +44,7 @@
val itemCount = intent.getIntExtra(ExtraItemCount, 3000)
setContent {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { itemCount }
Box(
modifier = Modifier
.fillMaxSize(),
@@ -56,8 +56,7 @@
.semantics { contentDescription = "Pager" }
.background(Color.White),
state = pagerState,
- pageSize = PageSize.Fill,
- pageCount = itemCount
+ pageSize = PageSize.Fill
) {
PagerItem(it)
}
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt
index 50fc66b..df252e7 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/PagerAsCarouselActivity.kt
@@ -45,7 +45,7 @@
val itemCount = intent.getIntExtra(ExtraItemCount, 3000)
setContent {
- val pagerState = rememberPagerState()
+ val pagerState = rememberPagerState { itemCount }
Box(
modifier = Modifier
.fillMaxSize()
@@ -57,7 +57,6 @@
.semantics { contentDescription = "Carousel" }
.background(Color.White),
state = pagerState,
- pageCount = itemCount,
pageSize = PageSize.Fixed(200.dp)
) {
PagerItem(it)
diff --git a/development/project-creator/create_project.py b/development/project-creator/create_project.py
index 8a121f8..7b0ddc4 100755
--- a/development/project-creator/create_project.py
+++ b/development/project-creator/create_project.py
@@ -151,9 +151,15 @@
def generate_package_name(group_id, artifact_id):
final_group_id_word = group_id.split(".")[-1]
- artifact_id_suffix = artifact_id.replace(final_group_id_word, "")
+ artifact_id_suffix = re.sub(r"\b%s\b" % final_group_id_word, "", artifact_id)
artifact_id_suffix = artifact_id_suffix.replace("-", ".")
- return group_id + artifact_id_suffix
+ if (final_group_id_word == artifact_id):
+ return group_id + artifact_id_suffix
+ elif (final_group_id_word != artifact_id):
+ if ("." in artifact_id_suffix):
+ return group_id + artifact_id_suffix
+ else:
+ return group_id + "." + artifact_id_suffix
def validate_name(group_id, artifact_id):
if not group_id.startswith("androidx."):
diff --git a/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingFoundationSample.kt b/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingFoundationSample.kt
index 8b55ca8..27b2fac 100644
--- a/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingFoundationSample.kt
+++ b/paging/paging-compose/samples/src/main/java/androidx/paging/compose/samples/PagingFoundationSample.kt
@@ -54,13 +54,12 @@
@Sampled
@Composable
public fun PagingWithHorizontalPager() {
- val pagerState = rememberPagerState()
val lazyPagingItems = pager.collectAsLazyPagingItems()
+ val pagerState = rememberPagerState { lazyPagingItems.itemCount }
HorizontalPager(
modifier = Modifier.fillMaxSize(),
state = pagerState,
- pageCount = lazyPagingItems.itemCount,
pageSize = PageSize.Fixed(200.dp),
key = lazyPagingItems.itemKey { it }
) { index ->
@@ -72,13 +71,12 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
public fun PagingWithVerticalPager() {
- val pagerState = rememberPagerState()
val lazyPagingItems = pager.collectAsLazyPagingItems()
+ val pagerState = rememberPagerState { lazyPagingItems.itemCount }
VerticalPager(
modifier = Modifier.fillMaxSize(),
state = pagerState,
- pageCount = lazyPagingItems.itemCount,
pageSize = PageSize.Fixed(200.dp),
key = lazyPagingItems.itemKey { it }
) { index ->