JetBrains/compose-multiplatform

I have an issue with a common ViewModel and navigation in my Compose Multiplatform application

meet2602 opened this issue · 3 comments

Describe the bug
I have an issue with a common ViewModel and navigation in my Compose Multiplatform application. The ViewModel is initialized in the common main:

val headlineViewModel = viewModel { HeadlineViewModel() }

I use two NavHost components for navigation:

@Composable
fun RootNavGraph() {
    val rootNavController = rememberNavController()
    NavHost(
        navController = rootNavController,
        route = Graph.RootGraph,
        startDestination = Graph.MainScreenGraph,
    ) {
        composable(route = Graph.MainScreenGraph) {
            MainScreen(rootNavController)
        }
        composable(route = NewsRouteScreen.NewsDetail.route) {
            rootNavController.previousBackStackEntry?.savedStateHandle?.get<String>("article")?.let { article ->
                val currentArticle: Article = Json.decodeFromString(article)
                ArticleDetailScreen(rootNavController, currentArticle)
            }
        }
    }
}

@Composable
fun MainNavGraph(
    rootNavController: NavHostController,
    homeNavController: NavHostController = rememberNavController(),
    innerPadding: PaddingValues,
) {
    NavHost(
        modifier = Modifier
            .fillMaxSize()
            .padding(innerPadding),
        navController = homeNavController,
        route = Graph.MainScreenGraph,
        startDestination = MainRouteScreen.Headline.route,
    ) {
        composable(route = MainRouteScreen.Headline.route) {
            HeadlineScreen(rootNavController)
        }
        composable(route = MainRouteScreen.Search.route) {
            SearchScreen(rootNavController)
        }
        composable(route = MainRouteScreen.Bookmark.route) {
            BookmarkScreen(rootNavController)
        }
    }
}

When navigating from the main screen to the detail screen:

navController.navigate(NewsRouteScreen.NewsDetail.route)

and then back to the main screen:

navController.navigateUp()

the MainNavGraph resets to the default start destination (MainRouteScreen.Headline.route). This causes the ViewModel to be recreated and any navigation state or position within the MainNavGraph is lost. How can I prevent the MainNavGraph from resetting and preserve the navigation state when navigating back?

Affected platforms

  • Android
  • Desktop (Windows, Linux, macOS)
  • iOS

Versions

compose-plugin = "1.6.11"
kotlin = "2.0.0"
lifecycleViewmodel = "2.8.2"
navigationCompose = "2.8.0-alpha02"

Additional context
I also tried using Koin in Android with viewModelOf and on other platforms with singleOf, and it works perfectly.

I'm having same issue. On my app, I have a bottom navigation bar, which leads to different screens, each with its own viewModel.
Every time I navigate to one of the screens, a new viewModel is created for that screen.
This issue is happening either on Android or IOS... with iOS I understand it still needs some work, as the lifecycle is different than Android. But I'm concerned why Android is having this issue as well.

The problem here is in unsupported restoring (de/serialization) of NavHostController outside of Android. It's tracked in #4735

Workaround 1: Use single NavHostController and define nested graphs via NavGraphBuilder.navigation() function. See documentation
Workaround 2: Move second rememberNavController() call out of wiped composition.

Closing as duplicate

But I'm concerned why Android is having this issue as well.

It's clearly a separate one. If it doesn't work on Android - it's not an issue for this fork, please report the bug to Google.

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.