首页 > 解决方案 > Jetpack Compose 中的作用域状态

问题描述

在所有应用程序中,总会有这三个状态范围: 状态

使用 Compose,可以通过以下方式实现“每屏幕状态”:

NavHost(navController, startDestination = startRoute) {
    ...
    composable(route) {
       ...
       val perScreenViewModel = viewModel()  // This will be different from
    }
    composable(route) {
       ...
       val perScreenViewModel = viewModel()  // this instance
    }
    ...
}

“应用程序状态”可以通过以下方式实现:

val appStateViewModel = viewModel()
NavHost(navController, startDestination = startRoute) {
    ...
}

但是“作用域状态”呢?我们如何在 Compose 中实现它?

标签: androidandroid-viewmodelandroid-navigationandroid-jetpack-compose

解决方案


这正是导航图范围视图模型的用途。

这包括两个步骤:

  1. 查找NavBackStackEntry与要将 ViewModel 范围限定为的图关联的

  2. 将其传递给viewModel().

对于第 1 部分),您有两个选择。如果您知道导航图的路线(通常您应该知道),您可以getBackStackEntry直接使用:

// Note that you must always use remember with getBackStackEntry
// as this ensures that the graph is always available, even while
// your destination is animated out after a popBackStack()
val navigationGraphEntry = remember {
  navController.getBackStackEntry("graph_route")
}
val navigationGraphScopedViewModel = viewModel(navigationGraphEntry)

但是,如果您想要更通用的东西,您可以使用目标本身中的信息检索回栈条目 - 它parent

fun NavBackStackEntry.rememberParentEntry(): NavBackStackEntry {
  // First, get the parent of the current destination
  // This always exists since every destination in your graph has a parent
  val parentId = navBackStackEntry.destination.parent!!.id

  // Now get the NavBackStackEntry associated with the parent
  // making sure to remember it
  return remember {
    navController.getBackStackEntry(parentId)
  }
}

这使您可以编写如下内容:

val parentEntry = it.rememberParentEntry()
val navigationGraphScopedViewModel = viewModel(parentEntry)

虽然parent目的地将等于简单导航图的根图,但当您使用嵌套导航时,父级是图的中间层之一:

NavHost(navController, startDestination = startRoute) {
    ...
  navigation(startDestination = nestedStartRoute, route = nestedRoute) {
    composable(route) {
      // This instance will be the same
      val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
    }
    composable(route) {
      // As this instance
      val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
    }
  }
  navigation(startDestination = nestedStartRoute, route = secondNestedRoute) {
    composable(route) {
        // But this instance is different
      val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
    }
  }
  composable(route) {
     // This is also different (the parent is the root graph)
     // but the root graph has the same scope as the whole NavHost
     // so this isn't particularly helpful
     val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
  }
  ...
}

请注意,您不仅限于直接父级:每个父级导航图都可用于提供更大的范围。


推荐阅读