JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.03k stars 1.16k forks source link

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

Closed meet2602 closed 2 months ago

meet2602 commented 3 months ago

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 MainNavGraphresets 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

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 viewModelOfand on other platforms with singleOf, and it works perfectly.

gustavopeq commented 3 months ago

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.

MatkovIvan commented 2 months ago

The problem here is in unsupported restoring (de/serialization) of NavHostController outside of Android. It's tracked in https://github.com/JetBrains/compose-multiplatform/issues/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.

okushnikov commented 2 months ago

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