Search code examples
kotlinretrofit2android-jetpack-composeandroid-jetpack-navigation

In Android jetpack compose, how to move the screen when I log in?


MainActivity

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject
    lateinit var connectivityManager: ConnectivityManager

    @Inject
    lateinit var settingsDataStore: SettingsDataStore

    override fun onStart() {
        super.onStart()
        connectivityManager.registerConnectionObserver(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        connectivityManager.unregisterConnectionObserver(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()
                NavHost(
                    navController = navController,
                    startDestination = Screen.Login.route) {
                    composable(
                        route = Screen.Login.route
                    ) { navBackStackEntry ->
                        val factory = HiltViewModelFactory(LocalContext.current, navBackStackEntry)
                        val viewModel: LoginViewModel = viewModel(key = "LoginViewModel", factory = factory)
                        LoginScreen(
                            isNetworkAvailable = connectivityManager.isNetworkAvailable.value,
                            onNavigateToHomeScreen = navController::navigate,
                            viewModel = viewModel
                        )
                    }

                    composable(
                        route = Screen.Home.route
                    ) { navBackStackEntry ->
                        val factory = HiltViewModelFactory(LocalContext.current, navBackStackEntry)
                        val viewModel: HomeViewModel = viewModel(key = "HomeViewModel", factory = factory)

                        HomeScreen(

                        )
                    }
                }
        }
    }
}

LoginScreen

@Composable
fun LoginScreen(
    isNetworkAvailable: Boolean,
    onNavigateToHomeScreen: (String) -> Unit,
    viewModel: LoginViewModel
) {

    val loading = viewModel.loading.value
    val user = viewModel.user.value

    Scaffold(
        modifier = Modifier.fillMaxSize()
    ) {
        if (!loading && user == null) {
            Button(onClick = { viewModel.onTriggerEvent(LoginEvent.AuthStateEvent) }) {
                Column(

                ) {
                    Text(text = "Log in")
                }
            }
        }else {
            user?.let {
                val route = Screen.Home.route
                onNavigateToHomeScreen(route)
            }
        }
    }

}

LoginViewModel

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val postLogin: PostLogin,
    private val connectivityManager: ConnectivityManager
) : ViewModel() {

    val user: MutableState<User?> = mutableStateOf(null)

    val loading = mutableStateOf(false)

    val onLoad: MutableState<Boolean> = mutableStateOf(false)

    fun onTriggerEvent(event: LoginEvent) {
        viewModelScope.launch {
            try {
                when(event) {
                    is LoginEvent.AuthStateEvent -> {
                        Log.d(TAG,"Phase 1")
                        if (user.value == null) {
                            val auth: Auth = Auth([***AuthData***])
                            postAuth(auth)
                        }
                    }
                }
            }catch (e: Exception) {
                Log.d(TAG, "launchJob: Exception: ${e}, ${e.cause}")
                e.printStackTrace()
            }
        }
    }

    private fun postAuth(auth: Auth) {
        Log.d(TAG,"Phase 2")
        postLogin.execute(auth, connectivityManager.isNetworkAvailable.value).onEach { dataState ->
            loading.value = dataState.loading

            dataState.data?.let { data ->
                user.value = data
                Log.d(TAG,"Phase 3")
                Log.d(TAG,"User Data File")
            }

            dataState.error?.let { error ->
                Log.d(TAG, "postAuth: ${error}")

            }
        }.launchIn(viewModelScope)
    }
}

LoginEvent

sealed class LoginEvent {

    object AuthStateEvent: LoginEvent()

}

PostLogin

class PostLogin(
    private val apiService: ApiService,
    private val authDtoMapper: AuthDtoMapper,
    private val userDtoMapper: UserDtoMapper
) {
    fun execute(
        auth: Auth,
        isNetworkAvailable: Boolean
    ): Flow<DataState<User>> = flow {
        //auth -> authDto -> InterActions -> userDto -> user
        try {
            emit(DataState.loading())
            delay(1000)

            if (isNetworkAvailable) {
                val networkUser = postLoginFromNetwork(auth)

                if (networkUser != null) {
                    emit(DataState.success(networkUser))
                }else {
                    emit(DataState.error<User>("User data is null"))
                }
            }else {
                emit(DataState.error<User>("Unable to connect network"))
            }

        }catch (e: Exception) {
            emit(DataState.error<User>(e.message?: "Unknown Error"))
        }
    }

    private suspend fun postLoginFromNetwork(auth: Auth): User {
        return userDtoMapper.mapToDomainModel(apiService.login(authDtoMapper.fromDomain(auth)))
    }
}

HomeScreen

@Composable
fun HomeScreen() {
    Card(
        modifier = Modifier.fillMaxSize()
    ) {
        Column(
            modifier = Modifier.fillMaxSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = "HomeScreen",
                fontWeight = FontWeight.Bold,
                fontSize = 40.sp
            )
        }
    }
}

Hello, I made the above code go to HomeScreen when login is successful by pressing the Login button on LoginScreen.

It was confirmed that the communication was smooth and the value was also brought.

However, when I tried debugging, I confirmed that this part was repeatedly done on Login Screen.

user?.let {
    val route = Screen.Home.route
    onNavigateToHomeScreen(route)
}

As you can see, when the user has a value, the screen is moved, but that part is repeatedly done, so the HomeScreen screen glitters like a flash.

That's why I'm asking you this question. When the value changes after communication, please tell me how to move the screen.


Solution

  • Use the LauchedEffect

    LaunchedEffect(user){
       user.let {
          navController.navigate("")
        }
    }