disable the NavHost back handling and drive back ourselves

Keeps the NavHost composed (the dropped-NavHost version left the still-transparent
overlay over black) while stopping the controller from winning the back over the
overlay's BackHandler. Back now: queue -> now playing -> pop the route.
This commit is contained in:
austinried
2026-10-04 00:29:58 +00:00
parent b849bd5c6f
commit 6e53b05973
+112 -111
View File
@@ -183,114 +183,108 @@ private fun MainNavigation() {
ArtworkTheme(artwork) { ArtworkTheme(artwork) {
Box(Modifier.fillMaxSize()) { Box(Modifier.fillMaxSize()) {
Column(Modifier.fillMaxSize().imePadding()) { Column(Modifier.fillMaxSize().imePadding()) {
Box(Modifier.weight(1f)) { NavHost(
// The now playing overlay covers this, and while it is up we drop the NavHost so navController = navController,
// it cannot steal the back from the overlay's BackHandler. modifier =
if (!nowPlayingOpen) { Modifier
NavHost( .weight(1f)
navController = navController, .then(
modifier = if (playerVisible) {
Modifier Modifier.consumeWindowInsets(WindowInsets.navigationBars)
.fillMaxSize() } else {
.then( Modifier
if (playerVisible) { },
Modifier.consumeWindowInsets(WindowInsets.navigationBars) ),
} else { startDestination = Routes.LIBRARY,
Modifier enterTransition = {
}, slideInHorizontally(initialOffsetX = { it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) +
), fadeIn(tween(NAVIGATION_DURATION_MS))
startDestination = Routes.LIBRARY, },
enterTransition = { exitTransition = {
slideInHorizontally(initialOffsetX = { it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) + slideOutHorizontally(targetOffsetX = { -it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) +
fadeIn(tween(NAVIGATION_DURATION_MS)) fadeOut(tween(NAVIGATION_DURATION_MS))
}, },
exitTransition = { popEnterTransition = {
slideOutHorizontally(targetOffsetX = { -it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) + slideInHorizontally(initialOffsetX = { -it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) +
fadeOut(tween(NAVIGATION_DURATION_MS)) fadeIn(tween(NAVIGATION_DURATION_MS))
}, },
popEnterTransition = { popExitTransition = {
slideInHorizontally(initialOffsetX = { -it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) + slideOutHorizontally(targetOffsetX = { it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) +
fadeIn(tween(NAVIGATION_DURATION_MS)) fadeOut(tween(NAVIGATION_DURATION_MS))
}, },
popExitTransition = { ) {
slideOutHorizontally(targetOffsetX = { it / 4 }, animationSpec = tween(NAVIGATION_DURATION_MS)) + composable(Routes.LIBRARY) {
fadeOut(tween(NAVIGATION_DURATION_MS)) LibraryRoute(
}, onAlbumClick = { album -> navController.navigate(Routes.album(album.id, album.coverArt)) },
) { onArtistClick = { artist -> navController.navigate(Routes.artist(artist.id, artist.coverArt)) },
composable(Routes.LIBRARY) { onPlaylistClick = { playlist -> navController.navigate(Routes.playlist(playlist.id)) },
LibraryRoute( onOpenSettings = { navController.navigate(Routes.SETTINGS) },
onAlbumClick = { album -> navController.navigate(Routes.album(album.id, album.coverArt)) }, bottomInset = bottomInset,
onArtistClick = { artist -> navController.navigate(Routes.artist(artist.id, artist.coverArt)) }, )
onPlaylistClick = { playlist -> navController.navigate(Routes.playlist(playlist.id)) }, }
onOpenSettings = { navController.navigate(Routes.SETTINGS) }, composable(Routes.SETTINGS) {
bottomInset = bottomInset, SettingsRoute(
) onAddServer = { navController.navigate(Routes.ADD_SERVER) },
} onBack = { navController.popBackStack() },
composable(Routes.SETTINGS) { )
SettingsRoute( }
onAddServer = { navController.navigate(Routes.ADD_SERVER) }, composable(
onBack = { navController.popBackStack() }, route = Routes.ALBUM_DETAIL,
) arguments =
} listOf(
composable( navArgument("albumId") { type = NavType.StringType },
route = Routes.ALBUM_DETAIL, navArgument("coverArt") {
arguments = type = NavType.StringType
listOf( defaultValue = ""
navArgument("albumId") { type = NavType.StringType }, },
navArgument("coverArt") { ),
type = NavType.StringType ) { entry ->
defaultValue = "" AlbumDetailRoute(
}, albumId = entry.arguments?.getString("albumId").orEmpty(),
), coverArtId =
) { entry -> entry.arguments
AlbumDetailRoute( ?.getString("coverArt")
albumId = entry.arguments?.getString("albumId").orEmpty(), .orEmpty()
coverArtId = .ifEmpty { null },
entry.arguments onBack = { navController.popBackStack() },
?.getString("coverArt") )
.orEmpty() }
.ifEmpty { null }, composable(
onBack = { navController.popBackStack() }, route = Routes.ARTIST_DETAIL,
) arguments =
} listOf(
composable( navArgument("artistId") { type = NavType.StringType },
route = Routes.ARTIST_DETAIL, navArgument("coverArt") {
arguments = type = NavType.StringType
listOf( defaultValue = ""
navArgument("artistId") { type = NavType.StringType }, },
navArgument("coverArt") { ),
type = NavType.StringType ) { entry ->
defaultValue = "" ArtistDetailRoute(
}, artistId = entry.arguments?.getString("artistId").orEmpty(),
), coverArtId =
) { entry -> entry.arguments
ArtistDetailRoute( ?.getString("coverArt")
artistId = entry.arguments?.getString("artistId").orEmpty(), .orEmpty()
coverArtId = .ifEmpty { null },
entry.arguments onBack = { navController.popBackStack() },
?.getString("coverArt") onAlbumClick = { album -> navController.navigate(Routes.album(album.id, album.coverArt)) },
.orEmpty() )
.ifEmpty { null }, }
onBack = { navController.popBackStack() }, composable(
onAlbumClick = { album -> navController.navigate(Routes.album(album.id, album.coverArt)) }, route = Routes.PLAYLIST_DETAIL,
) arguments = listOf(navArgument("playlistId") { type = NavType.StringType }),
} ) { entry ->
composable( PlaylistDetailRoute(
route = Routes.PLAYLIST_DETAIL, playlistId = entry.arguments?.getString("playlistId").orEmpty(),
arguments = listOf(navArgument("playlistId") { type = NavType.StringType }), onBack = { navController.popBackStack() },
) { entry -> )
PlaylistDetailRoute( }
playlistId = entry.arguments?.getString("playlistId").orEmpty(), composable(Routes.ADD_SERVER) {
onBack = { navController.popBackStack() }, AddSourceRoute(
) onSaved = { navController.popBackStack() },
} onBack = { navController.popBackStack() },
composable(Routes.ADD_SERVER) { )
AddSourceRoute(
onSaved = { navController.popBackStack() },
onBack = { navController.popBackStack() },
)
}
}
} }
} }
@@ -377,8 +371,15 @@ private fun MainNavigation() {
} }
} }
BackHandler(enabled = showingQueue) { showingQueue = false } // The NavHost registers its own back callback, and OnBackPressedDispatcher hands the back to
BackHandler(enabled = nowPlayingOpen && !showingQueue) { // the last-registered enabled callback, so it can win over the overlay's BackHandler and pop
settleNowPlaying(false) // the route underneath. Disable the controller's back and drive navigation ourselves.
LaunchedEffect(navController) { navController.enableOnBackPressed(false) }
BackHandler(enabled = showingQueue || nowPlayingOpen || navController.previousBackStackEntry != null) {
when {
showingQueue -> showingQueue = false
nowPlayingOpen -> settleNowPlaying(false)
else -> navController.popBackStack()
}
} }
} }