lostintranslation
lostintranslation

Reputation: 24583

Jetpack Compose Navigation endless loop

I am trying to show a walkthrough of my app on initial start up. I am trying to present 3 screens

Welcome Screen1 Screen2

I have this navigation graph

composable(Routes.Welcome.name) {
   WelcomeScreen(
      done = {
         navController.navigate(Routes.Screen1.name)
      }
   )
}

composable(Routes.Screen1.name) {
   Screen1(
      done = {
         navController.navigate(Routes.Screen2.name) {
            popUpTo(Routes.Welcome.name)
         }
      }
   )
}

composable(Routes.Screen2.name) {
   Screen2(
      done = {
         navController.navigate(Routes.Screen3.name) {
            popUpTo(Routes.Welcome.name)
         }
      }
   )
}

composable(Routes.Screen3.name) {
   Screen1(
      done = {
         navController.navigate(Routes.Main.name)
      }
   )
}

However when I am done with Screen1 and I try to navigate to Screen2 I end up in an infinite loop with Screen1 done continuously being called while Screen2 is presented.

@Composable
fun Screen1(
   done: () -> Unit,
   viewModel: StartupViewModel = hiltViewModel()
) {
   val screen1 by viewModel.screen1.observeAsState()

   if (screen1 != true) {
         Button(onClick = { viewModel.setScreen1(true) }) {
            Text(text = "Go to screen 2")
         }
   } else {
      // once screen1 is set in viewmodel I should end up here.
      // let my parent handle navigation to next screen.
      done()
   }
}

Upvotes: 4

Views: 1305

Answers (1)

z.g.y
z.g.y

Reputation: 6207

It looks like a similar issue like this and this.

Based on the official Docs,

You should only call navigate() as part of a callback and not as part of your composable itself, to avoid calling navigate() on every recomposition.

The culprit is in the entire if-else block.

val screen1 by viewModel.screen1.observeAsState() 

if (screen1 != true) {
    ...
 } else {
    ...
    done()
}

At first pass of composition, the if{…} block will execute, but when you update the screen1 state it will satisfy the else{…} block calling your navigation, it will then call a re-composition of the NavHost which in turn call this composable again and because screen1 != true stills evaluate to false it will call the else{…} block again, now your navigation is on a vicious loop.

I would suggest doing this inside a LaunchedEffect.

Please check this, the code in question looks closely similar to yours.

Upvotes: 1

Related Questions