State

package org.sopt.and.presentation.sign.state

data class SignInState (
    val username: String = "",
    val password: String = ""
)

ViewModel

@HiltViewModel
class SignInViewModel @Inject constructor(
    private val loginUseCase: LoginUseCase
) : ViewModel() {
    val _signInState = MutableStateFlow(SignInState())
    val signInState = _signInState.asStateFlow()

    private val _loginUserResultState = MutableStateFlow<UserLoginResult?>(null)
    val loginUserResultState: StateFlow<UserLoginResult?> = _loginUserResultState

    private val _errorMessageState = MutableStateFlow<String?>(null)
    val errorMessageState: StateFlow<String?> = _errorMessageState

    private val _signInSuccess =  MutableSharedFlow<Boolean>()
    val signInSuccess: SharedFlow<Boolean> = _signInSuccess

    fun updateUserName(newUserName: String) {
        _signInState.update { currentState ->
            currentState.copy(
                username = newUserName
            )
        }
    }
    fun updatePassword(newPassword: String) {
        _signInState.update { currentState ->
            currentState.copy(
                password = newPassword
            )
        }
    }

    private suspend fun setSignInSuccess(value: Boolean) {
        _signInSuccess.emit(value)
    }

    fun signIn() {
        viewModelScope.launch {
            when (val result = loginUseCase(
                UserData(
                    _signInState.value.username,
                    _signInState.value.password,
                    ""
                )
            )
            ) {
                is BaseResult.Success -> {
                    _loginUserResultState.value = result.data
                    _errorMessageState.value = null
                    setSignInSuccess(true)
                }
                is BaseResult.Error -> {
                    _loginUserResultState.value = null
                    _errorMessageState.value = result.message
                    setSignInSuccess(false)
                }
            }
        }
    }

    suspend fun resetSignInSuccess() {
        setSignInSuccess(false)
    }
}

Screen

@Composable
fun SignInScreen(
    navigateToMy: () -> Unit,
    navigateToSignUp: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: SignInViewModel = hiltViewModel(),
) {
    val signInState by viewModel.signInState.collectAsState()
    val loginState by viewModel.loginUserResultState.collectAsState()
    val context = LocalContext.current

    LaunchedEffect(Unit) {
        viewModel.signInSuccess.collectLatest { success ->
            if (success) {
                loginState?.let { loginState ->
                    PreferenceUtils.saveUserToken(context, loginState.token)
                }
                CoroutineScope(Dispatchers.Main).launch {
                    SnackBarUtils.showSnackBar(
                        message = context.getString(R.string.sign_in_snackbar_login_success),
                        actionLabel = context.getString(R.string.sign_in_snackbar_action_close)
                    )
                }
                navigateToMy()
                viewModel.resetSignInSuccess()
            } else {
                viewModel.errorMessageState.value?.let { message ->
                    CoroutineScope(Dispatchers.Main).launch {
                        SnackBarUtils.showSnackBar(
                            message = message,
                            actionLabel = context.getString(R.string.sign_in_snackbar_action_close)
                        )
                    }
                }
            }
        }
    }

    Column(
        modifier = modifier
            .fillMaxSize()
            .background(WavveBg)
    ) {
        BackButtonTopBar({ /*TODO : 뒤로가기처리*/ })
        Column(
            modifier = modifier
                .fillMaxSize()
                .background(WavveBg)
                .padding(16.dp)
        ) {
            WavveCommonTextField(
                value = signInState.username,
                onValueChange = **viewModel::updateUserName**,
                hint = stringResource(R.string.sign_in_text_field_id_hint)
            )
            Spacer(modifier = Modifier.height(4.dp))
            WavveCommonPasswordField(
                value = signInState.password,
                onValueChange = viewModel::updatePassword,
                hint = stringResource(R.string.sign_in_text_field_password_hint)
            )

            Spacer(modifier = Modifier.height(24.dp))

            WavveBasicButton(
                text = stringResource(R.string.sign_in_text_login),
                onClick = { viewModel.signIn() },
                modifier = Modifier
            )

            Spacer(modifier = Modifier.height(16.dp))

            Row(
                modifier = Modifier
                    .align(Alignment.CenterHorizontally),
                horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
            ) {
                Text(
                    text = stringResource(R.string.sign_in_text_find_id),
                    fontSize = 12.sp,
                    color = Gray3,
                )

                Text(
                    text = stringResource(R.string.sign_in_text_divider),
                    fontSize = 12.sp,
                    color = Gray3,
                )

                Text(
                    text = stringResource(R.string.sign_in_text_password_reset),
                    fontSize = 12.sp,
                    color = Gray3,
                )

                Text(
                    text = stringResource(R.string.sign_in_text_divider),
                    fontSize = 12.sp,
                    color = Gray3,
                )

                Text(
                    text = stringResource(R.string.sign_in_text_sign_up),
                    fontSize = 12.sp,
                    color = Gray3,
                    modifier = Modifier
                        .noRippleClickable(navigateToSignUp)
                )
            }

            Spacer(modifier = Modifier.height(12.dp))

            Row(
                modifier = modifier
                    .fillMaxWidth()
                    .wrapContentHeight(),
                verticalAlignment = Alignment.CenterVertically

            ) {
                HorizontalDivider(
                    thickness = 0.5.dp,
                    color = Gray4,
                    modifier = Modifier
                        .weight(1f)
                )

                Text(
                    text = stringResource(R.string.sign_in_text_other_service),
                    color = Gray3,
                    fontSize = 12.sp,
                    modifier = Modifier.padding(horizontal = 8.dp)
                )

                HorizontalDivider(
                    thickness = 0.5.dp,
                    color = Gray4,
                    modifier = Modifier
                        .weight(1f)
                )
            }

            ServiceAccountItemRow()

            Spacer(Modifier.height(16.dp))

            Text(
                text = stringResource(R.string.sign_in_text_information),
                color = Gray3,
                fontSize = 12.sp,
                modifier = Modifier.padding(horizontal = 8.dp)
            )

        }
    }
}
//    TODO - 얘네를 우선 완성해야함.
//    {
//        "phoneNumber" : "01064446919",
//        "nickname" : "지혀닝",
//        "birthYear" : 2000,
//        "gender" : false,
//        "profileImage" : "image2",
//        "region" : "성북/강북/노원/도봉/중랑"
//    }