渐进式编程课
KMP 目录
第 28 课2026.08.13

普通详情页需要返回上一页,而登录、支付完成等一次性流程结束后,不应该再返回旧页面。正确整理返回栈可以避免“登录成功后按返回键又看到登录页”。

第 28 课:Compose 导航返回栈

知识点

使用 popBackStack() 和 popUpTo() 管理 Compose Multiplatform 的页面返回栈。

用途

普通详情页需要返回上一页,而登录、支付完成等一次性流程结束后,不应该再返回旧页面。正确整理返回栈可以避免“登录成功后按返回键又看到登录页”。

核心概念

  • navigate() 默认把新目的地压到栈顶。
  • popBackStack() 移除当前目的地,回到前一个页面。
  • popUpTo<Route>() 在跳转时弹出栈中指定路由上方的页面。
  • inclusive = true 表示连指定路由本身也一起移除。
  • launchSingleTop = true 避免栈顶重复出现同一个目的地。

示例代码

登录成功后跳到首页,并把登录页从返回栈中移除:

kotlin
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlinx.serialization.Serializable

@Serializable
data object Login

@Serializable
data object Home

@Serializable
data object Detail

@Composable
fun App() {
    val navController = rememberNavController()

    NavHost(
        navController = navController,
        startDestination = Login,
    ) {
        composable<Login> {
            Button(
                onClick = {
                    navController.navigate(Home) {
                        popUpTo<Login> {
                            inclusive = true
                        }
                        launchSingleTop = true
                    }
                },
            ) {
                Text("登录")
            }
        }

        composable<Home> {
            Button(onClick = { navController.navigate(Detail) }) {
                Text("打开详情")
            }
        }

        composable<Detail> {
            Button(onClick = { navController.popBackStack() }) {
                Text("返回首页")
            }
        }
    }
}

执行登录跳转后,栈从 [Login] 变为 [Home];进入详情页后变为 [Home, Detail],点击返回则恢复为 [Home]。

5 分钟练习

新增 Checkout 和 Receipt 两个路由。支付完成后从 Checkout 跳到 Receipt,同时移除 Checkout,避免用户返回支付页。

参考答案

kotlin
@Serializable
data object Checkout

@Serializable
data object Receipt

composable<Checkout> {
    Button(
        onClick = {
            navController.navigate(Receipt) {
                popUpTo<Checkout> {
                    inclusive = true
                }
            }
        },
    ) {
        Text("完成支付")
    }
}

composable<Receipt> {
    Text("支付成功")
}

与上一课的联系

上一课学习了用类型安全路由传递页面参数;今天继续管理这些路由在返回栈中的生命周期,决定哪些页面应保留、哪些一次性页面应在完成后移除。

参考资料