mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
fix: keep back arrow visible until the exiting screen finishes leaving
canPop() read the globally-current back-stack entry via currentBackStackEntryAsState(). A pop commits the instant it is accepted — most visibly during a predictive back-swipe, whose exit animation is long and finger-driven — so controller.currentBackStackEntry flips to the destination while the screen being dismissed is still on screen, sliding out and still composing its top bar. Evaluating canPop against the incoming destination there dropped the back arrow (and re-showed the bottom bar) before the outgoing screen had finished leaving, which is exactly the flicker seen when back-swiping to Home or a bottom-nav root. Evaluate poppability against the screen's own NavBackStackEntry — the one the NavHost provides to each destination via LocalViewModelStoreOwner — which is intrinsic to that screen and never changes for the life of its composition. The arrow now stays put until the screen itself is gone. Callers outside a NavHost destination (shell chrome, drawer) see the account-scoped owner instead of an entry and fall back to the previous globally-current behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011matx1mGJ6ZyS5dS77EQUo
This commit is contained in:
@@ -28,9 +28,10 @@ import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.vitorpamplona.amethyst.ui.navigation.BOTTOM_NAV_ROOT_KEY
|
||||
import com.vitorpamplona.amethyst.ui.navigation.isBottomNavRoot
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -115,19 +116,35 @@ class Nav(
|
||||
|
||||
@Composable
|
||||
override fun canPop(): Boolean {
|
||||
// Observe the current entry as State so consumers recompose when the
|
||||
// back stack settles after a navigation or back-swipe transition.
|
||||
// A non-reactive read would leave a stale value behind: e.g. on
|
||||
// back-swipe to Home, previousBackStackEntry is still the popping
|
||||
// entry until the gesture finishes, and nothing would re-evaluate
|
||||
// canPop afterwards.
|
||||
val current by controller.currentBackStackEntryAsState()
|
||||
val entry = current ?: return false
|
||||
// Decide the back arrow / bottom-bar visibility from THIS screen's own
|
||||
// back-stack entry — the one the NavHost hands to each destination
|
||||
// through LocalViewModelStoreOwner — instead of the globally-current
|
||||
// entry.
|
||||
//
|
||||
// A pop commits the moment it is accepted (most visibly during a
|
||||
// predictive back-swipe, whose exit animation is long and finger-driven):
|
||||
// controller.currentBackStackEntry flips to the destination while the
|
||||
// screen being dismissed is still on screen, sliding out and still
|
||||
// composing its top bar. Reading the global entry there re-evaluated
|
||||
// canPop against the incoming destination and dropped the arrow before
|
||||
// the outgoing screen had finished leaving. An entry is intrinsic to its
|
||||
// screen and never changes for the life of that composition, so the arrow
|
||||
// now stays put until the screen itself is gone.
|
||||
//
|
||||
// Outside a NavHost destination (shell chrome, drawer) the current owner
|
||||
// is the account-scoped ViewModelStoreOwner, not an entry; fall back to
|
||||
// the globally-current entry so those callers keep their prior behavior.
|
||||
val entry =
|
||||
(LocalViewModelStoreOwner.current as? NavBackStackEntry)
|
||||
?: controller.currentBackStackEntry
|
||||
?: return false
|
||||
|
||||
// Hidden on tab roots (reached via the bottom nav) and on Home (the
|
||||
// graph's start destination): nothing sits below either that a back
|
||||
// arrow could return to. Every other entry is a push on top of Home,
|
||||
// so it can always pop.
|
||||
if (entry.isBottomNavRoot()) return false
|
||||
// Home is the graph's start destination and nothing can sit below
|
||||
// it, so a back arrow there is never meaningful.
|
||||
if (entry.destination.id == controller.graph.findStartDestination().id) return false
|
||||
return controller.previousBackStackEntry != null
|
||||
return entry.destination.id != controller.graph.findStartDestination().id
|
||||
}
|
||||
|
||||
override fun popBack() {
|
||||
|
||||
Reference in New Issue
Block a user