Ad

Composable Is Recomposing Endlessly After Flow Collect

My composable is recomposing endlessly after flow collect and navigating to a new screen. I can't understand why.

I'm using Firebase for Auth with Email and Password.

I had to put some Log.i to test my function and my composable, and yes, my Main composable (SignUp) is recomposing endlessly after navigating.

ViewModel

// Firebase auth
private val _signUpState = mutableStateOf<Resources<Any>>(Resources.success(false))
val signUpState: State<Resources<Any>> = _signUpState

fun firebaseSignUp(email: String, password: String) {
    job = viewModelScope.launch(Dispatchers.IO) {
        firebaseAuth.firebaseSignUp(email = email, password = password).collect {
            _signUpState.value = it
            Log.i("balito", "polipop")
        }
    }
}

fun stop() {
    job?.cancel()
}

SignUp

@Composable
fun SignUp(
    navController: NavController,
    signUpViewModel: SignUpViewModel = hiltViewModel()
) {
    val localFocusManager = LocalFocusManager.current
    Log.i("salut", "salut toi")
    Column(
        modifier = Modifier
            .fillMaxSize()
            .systemBarsPadding()
            .padding(16.dp)
            .background(color = PrimaryColor)
    ) {
        BackButton(navController = navController)
        Spacer(modifier = Modifier.height(30.dp))
        Text(
            text = stringResource(id = R.string.sinscrire),
            fontFamily = visby,
            fontWeight = FontWeight.SemiBold,
            fontSize = 28.sp,
            color = Color.White
        )
        Spacer(modifier = Modifier.height(2.dp))
        Text(
            text = stringResource(R.string.prenez_votre_sante_en_main),
            fontFamily = visby,
            fontWeight = FontWeight.SemiBold,
            fontSize = 20.sp,
            color = Grey
        )
        Spacer(modifier = Modifier.height(20.dp))
        Email(signUpViewModel = signUpViewModel, localFocusManager = localFocusManager)
        Spacer(modifier = Modifier.height(16.dp))
        Password(signUpViewModel = signUpViewModel, localFocusManager = localFocusManager)
        Spacer(modifier = Modifier.height(30.dp))
        Button(value = stringResource(R.string.continuer), type = Type.Valid.name) {
            localFocusManager.clearFocus()
            signUpViewModel.firebaseSignUp(signUpViewModel.emailInput.value, signUpViewModel.passwordInput.value)
        }
        Spacer(modifier = Modifier.height(16.dp))
        Button(value = stringResource(R.string.inscription_avec_google), type = Type.Other.name) {

        }
        Spacer(modifier = Modifier.weight(1f))
        Box(
            modifier = Modifier
                .fillMaxWidth(),
            contentAlignment = Alignment.Center
        ) {
            ClickableTextInfo(stringResource(id = R.string.deja_un_compte_se_connecter), onClick = {})
        }
    }
    Response(navController = navController, signUpViewModel = signUpViewModel)
    DisposableEffect(key1 = signUpViewModel.signUpState.value == Resources.success(true)) {
        onDispose {
            signUpViewModel.stop()
            Log.i("fin", "fin")
        }
    }
}

@Composable
private fun Response(
    navController: NavController,
    signUpViewModel: SignUpViewModel
) {
    when (val response = signUpViewModel.signUpState.value) {
        is Resources.Loading<*> -> {
            //WaitingLoaderProgress(loading = true)
        }
        is Resources.Success<*> -> {
            response.data.also {
                Log.i("lolipop", "lolipopi")
                if (it == true) {
                    navController.navigate(Screen.SignUpConfirmation.route)
                }
            }
        }
        is Resources.Failure<*> -> {
//            response.throwable.also {
//                Log.d(TAG, it)
//            }
        }
    }
}
Ad

Answer

During navigation transition recomposition happens multiple times because of animations, and you call navController.navigate on each recomposition.

You should not cause side effects or change the state directly from the composable builder, because this will be performed on each recomposition, which is not expected in cases like animation.

Instead you should use side effects. In your case, LaunchedEffect should be used.

if (response.data) {
    LaunchedEffect(Unit) {
        Log.i("lolipop", "lolipopi")
        navController.navigate(Screen.SignUpConfirmation.route)
    }
}
Ad
source: stackoverflow.com
Ad