keep the overlay back callback last in the dispatcher

The NavHost registers its own back callback after ours, so on a pushed route it won
the back and popped the route under the now playing overlay. Re-register our callback
on every navigation (DisposableEffect on the current route) so it is always the most
recent, and toggle its enabled state with the overlay/queue.
This commit is contained in:
austinried
2026-10-04 00:29:58 +00:00
parent 6e53b05973
commit ad5660381f
@@ -1,7 +1,8 @@
package com.subtracks.ui package com.subtracks.ui
import android.net.Uri import android.net.Uri
import androidx.activity.compose.BackHandler import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animate import androidx.compose.animation.core.animate
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
@@ -31,7 +32,9 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBars
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -371,15 +374,27 @@ private fun MainNavigation() {
} }
} }
// The NavHost registers its own back callback, and OnBackPressedDispatcher hands the back to // The NavHost registers its back callback after ours, so on a pushed route it wins and pops the
// the last-registered enabled callback, so it can win over the overlay's BackHandler and pop // route under the now playing overlay. Re-register ours on every navigation so it is the most
// the route underneath. Disable the controller's back and drive navigation ourselves. // recent callback in the dispatcher and gets the back first.
LaunchedEffect(navController) { navController.enableOnBackPressed(false) } val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
BackHandler(enabled = showingQueue || nowPlayingOpen || navController.previousBackStackEntry != null) { val backCallback =
remember {
object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
when { when {
showingQueue -> showingQueue = false showingQueue -> showingQueue = false
nowPlayingOpen -> settleNowPlaying(false) nowPlayingOpen -> settleNowPlaying(false)
else -> navController.popBackStack() else -> navController.popBackStack()
} }
} }
}
}
SideEffect {
backCallback.isEnabled = showingQueue || nowPlayingOpen || navController.previousBackStackEntry != null
}
DisposableEffect(backDispatcher, currentRoute) {
backDispatcher?.addCallback(backCallback)
onDispose { backCallback.remove() }
}
} }