Problem changing BasicSwipeToDismissBox background...
# compose-wear
f
Problem changing BasicSwipeToDismissBox background color -->
Hi, i was trying to change the background color of the BasicSwipeToDismissBox by editing LocalSwipeToDismissContentScrimColor and LocalSwipeToDismissContentScrimColor with the "provides" method. Unfortunately, this doesn't work. Any suggestions?
Context: My app has a PagerScreen with edgeSwipeToDismiss modifier to handle the swipe back on the left. With the default implementation of SwipeDismissableNavHost, after swiping, i get a black screen with the following warning:
Current backstack entry is empty. Please ensure:
1. The current WearNavigator navigation backstack is not empty (e.g. by using androidx.wear.compose.navigation.composable to build your nav graph).
2. The last entry is not popped prior to navigation (instead, use navigate with popUpTo).
3. If the activity uses FLAG_ACTIVITY_NEW_TASK you should also set FLAG_ACTIVITY_CLEAR_TASK to maintain the backstack consistency.
If i wrap the navhost in a BasicSwipeToDismissBox (even if it is already in the SwipeDismissableNavHost), sharing the swipe state of the navhost, and i implement a custom LaunchedEffect, i can come back to the watchface, but a black screen appears for just a second.
y
I hope you can get it working without the additional SwipeToDismissBox. So maybe share a repro of that or file a bug.
s
+1. NavHost already has SwipeToDismissBox inside it. So, you don't need an additional SwipeToDismissBox on top of it.
f
@Shubham as i said, i know that SwipeToDismissBox is into NavHost, but it doesn't go to the home with a back swipe. @yschimke nope :/...further more, with the additional SwipeToDismissBox and the onBackPressed into the onDismiss, i can go to the home yes but, when i resume the app, i see only a black screen. I have to close the app into the onDismiss. I will provide you a repro later this morning :)
πŸ™πŸ» 1
@yschimke i updated the repro of the HorizontalPagerBug (btw, i can still reproduce it). There's a bool "workaround" that you can change to see the diff with the default implementation in Horologist. ref: https://github.com/feduss/HorizontalPagerBug
s
From the code screenshot above, I don't think you should need to have the LaunchedEffect(swipeToDismissBoxState.currentValue) which is going to trigger onBackPressed for each swipe to dismiss. That will be handled in SwipeDismissableNavHost - and would explain why your backstack is empty, because it's being popped twice when you swipe to dismiss.
f
Unfortunately, it's empty even without that LaunchedEffect
The implementation of that LaunchedEffect is a workaround precisely because of that problem (the empty backstack)
In the linked repro, i also replaced onBackPressed with a function to close the app...but i repeat, this isn't the behavior i want: i want to put the app in background and go back to the watchface
s
Hi - I downloaded your repo - removing the workaround and also removing the use of Modifier.edgeSwipeToDismiss mostly works for me, but I still see some LTR swipes on the first page of the Pager that ought to close the app but just 'nudge' the Pager instead. Still investigating what can be done to handle the swipes consistently.
f
Thanks for the update Steve :)! I'm here if you need some kind of tests.
s
Hi - I've discussed this issue with the team that owns Pager. They would appreciate it if you could run a test, which is to override the touch slop on the pager screens to double its original value (also remove your workaround and the use of Modifier.edgeSwipeToDismiss). Please could you let us know what differences this makes in your repro app? Thanks in advance. You could use this utility code to help:
Copy code
@Composable
internal fun CustomTouchSlopProvider(
    newTouchSlop: Float,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(
        LocalViewConfiguration provides CustomTouchSlop(
            newTouchSlop,
            LocalViewConfiguration.current
        )
    ) {
        content()
    }
}

class CustomTouchSlop(
    private val customTouchSlop: Float,
    currentConfiguration: ViewConfiguration
) : ViewConfiguration by currentConfiguration {
    override val touchSlop: Float
        get() = customTouchSlop
}
and then wrap the current content like this:
Copy code
val originalTouchSlop = LocalViewConfiguration.current.touchSlop
CustomTouchSlopProvider(newTouchSlop = originalTouchSlop * 2) {
  // content goes here
}
πŸ‘€ 1
f
Hi Steve, thanks for the answer and the code. Unfortunately, i don't see any different in the behaviour πŸ˜•...here's the repro --> https://github.com/feduss/HorizontalPagerBug
s
Thanks - please could you try moving the touch slop override so that it's around the whole PagerScreen?
πŸ‘€ 1
f
Hi, i think the output is the same πŸ˜•
s
Interesting - we've been talking at cross-purposes, the video above is for b/303807950 isn't it, which is the issue where the Pager doesn't respond to the next tap. That should be fixed in Horologist with the change to HorizontalPagerDefaults.flingParams, but clearly isn't fixed in your video. It would be interesting if you could experiment with an even shorter duration for the snapAnimationSpec (it's
Copy code
tween(150, 0)
in the library) - just to confirm if the problem you see is that the pager is not settling before the tap on the screen.
f
Oh ok sorry, i think i misunderstood the scope of the test! Did i? So, is the slop override meant to fix the LTR swipe, to close the app? I will try the test fix for the pager tap later, thanks! :)
@stevebower i tried 75ms, 10 and 1, without any visible changes...i don't think the prob is in the animation, at this point (i forked horologist and put my sample into it, tweaking the snapAnimationSpec)
s
Hi - regarding the original problem about configuring the Pager so that you can use swipe-to-dismiss to exit, without navigating to a blank screen. We've looked at this with the Compose Foundation team and the recommendation is to define an edge-zone for Pager of about 15% (like the one defined for BasicSwipeToDismissBox) using Modifier.pointerInput. Then set Modifier.semantics to indicate whether horizontal scrolling is active (this is used by the system swipe-to-dismiss) and also set userScrollEnabled on the HorizontalPager. In my tests, adjusting the touch slop also makes this more reliable. Note that this is for the case where your Pager is used at the top of the navigation hierarchy. If it is on any other screen, adding Modifier.edgeSwipeToDismiss to the HorizontalPager works fine and the workaround is not necessary. Here's some demo code that does the above:
Copy code
class MainActivity : ComponentActivity() {

    @OptIn(ExperimentalFoundationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent(parent = null) {
            MaterialTheme {
                val navController = rememberSwipeDismissableNavController()
                SwipeDismissableNavHost(
                    navController = navController,
                    startDestination = PAGER_SCREEN
                ) {
                    composable(PAGER_SCREEN) {
                        val originalTouchSlop = LocalViewConfiguration.current.touchSlop
                        CustomTouchSlopProvider(newTouchSlop = originalTouchSlop * 2) {
                            val pagesCount = 3
                            val pagerState = rememberPagerState { pagesCount }

                            val screenWidth = with(LocalDensity.current) {
                                LocalConfiguration.current.screenWidthDp.dp.toPx()
                            }
                            var allowPaging by remember { mutableStateOf(true) }

                            HorizontalPager(
                                modifier = Modifier
                                    .fillMaxSize()
                                    .pointerInput(screenWidth) {
                                        coroutineScope {
                                            awaitEachGesture {
                                                allowPaging = true
                                                val firstDown =
                                                    awaitFirstDown(false, PointerEventPass.Initial)
                                                val xPosition = firstDown.position.x
                                                // Define edge zone of 15%
                                                allowPaging = xPosition > screenWidth * 0.15f
                                            }
                                        }
                                    }
                                    .semantics {
                                        horizontalScrollAxisRange = if (allowPaging) {
                                            ScrollAxisRange(value = { pagerState.currentPage.toFloat() },
                                                maxValue = { 3f })
                                        } else {
                                            // signals system swipe to dismiss that they can take over
                                            ScrollAxisRange(value = { 0f },
                                                maxValue = { 0f })
                                        }
                                    },
                                state = pagerState,
                                flingBehavior =
                                PagerDefaults.flingBehavior(
                                    pagerState,
                                    snapAnimationSpec = tween(150, 0),
                                ),
                                userScrollEnabled = allowPaging
                            ) {
                                Centralize {
                                    ListHeader { Text("Page $it") }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun Centralize(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        content = content
    )
}

@Composable
private fun CustomTouchSlopProvider(
    newTouchSlop: Float,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(
        LocalViewConfiguration provides CustomTouchSlop(
            newTouchSlop,
            LocalViewConfiguration.current
        )
    ) {
        content()
    }
}

private class CustomTouchSlop(
    private val customTouchSlop: Float,
    currentConfiguration: ViewConfiguration
) : ViewConfiguration by currentConfiguration {
    override val touchSlop: Float
        get() = customTouchSlop
}

private const val PAGER_SCREEN = "pager_screen"
f
Hi @stevebower, thanks for the answer! Your solution seems working fine! I created my custom SwipeToClosePagerScreen, mixing both horologist's PagerScreen and your solution to test...and i noticed that b/303807950 has been fixed with a 10ms duration. FYI (i guess?) @yschimke
s
Hi again - regarding b/303807950, which is the issue where the Pager doesn't respond to the next tap, the Compose Foundation team have released a change in https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.7.0-alpha03 which should help with that (it isn't mentioned explicitly in the release notes, but the change concerns nested scrolling).
b
πŸ‘‹ sorry to resuscitate an old thread - I seem to have the same issue of "swipe-to-dismiss on a Pager at the top of the navigation hierarchy navigates to a blank screen when using `edgeSwipeToDismiss`". I see your workaround @stevebower (which I haven't tried but assume it works). But is this going to 'just work' without the workaround in a upcoming version of the libraries? Is there an issue already?
f
Hi @stevebower. Sorry, i was super busy the last month and i couldn't work on my project. I was wondering if there is any new about the b/303807950, thanks πŸ™‚ (@bod, see https://issuetracker.google.com/issues/303807950)
b
Thanks @FEDUSS - correct me if I'm wrong but I think this ticket is about something else? My issue is only about having a pager and
SwipeDismissableNavHost
and trying to dismiss from the edge.
f
@bod Maybe we discussed of the problem here on slack XD...if you check prev answers, i said i created the SwipeToClosePagerScreen to fix the problem. You can find it in the repro --> https://github.com/feduss/HorizontalPagerBug/blob/master/app/src/main/java/com/feduss/horizontalpagerbug/SwipeToClosePagerScreen.kt But be careful, because that repro is unstable if you change page with a swipe, because i reused it to show a bug with SwipeToRevealCard.
b
Thanks a lot @FEDUSS I'll check it out. But I just want to make sure there's a ticket for the issue, and it will be fixed at some point in the future without needing workarounds πŸ˜„ If there's no ticket yet I can totally create it myself, no problem.
f
there's no ticket for that bug, but i personally consider it related to my ticket XD...feel free to open another ticket if you want
πŸ‘ 1
b
Yeah I mean I don't want too many tickets to be opened, but at the same time it's probably good to have "single issue" tickets.
πŸ‘ 1
158 Views