Last active
January 28, 2024 14:14
-
-
Save luck-alex13/78c4b01e5b7b5472dfe17aaa8fd07d97 to your computer and use it in GitHub Desktop.
MutableStateFlow with Fragment and ViewModel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@AndroidEntryPoint | |
class LoginFragment : BaseBindingFragment<LoginFragmentBinding>() { | |
companion object { | |
fun screen() = FragmentScreen { LoginFragment() } | |
} | |
private val viewModel: LoginViewModel by viewModels() | |
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LoginFragmentBinding = | |
LoginFragmentBinding::inflate | |
private val phoneMask = MaskImpl.createTerminated(PredefinedSlots.RUS_PHONE_NUMBER) | |
private val formatWatcher: FormatWatcher = MaskFormatWatcher(phoneMask) | |
override fun setupUI(binding: LoginFragmentBinding) { | |
binding.apply { | |
formatWatcher.installOnAndFill(phoneEditText) | |
phoneEditText.doAfterTextChanged { | |
phoneEditText.background = drawableCompat(R.drawable.edit_background) | |
} | |
passwordField.doAfterTextChanged { | |
passwordField.background = drawableCompat(R.drawable.edit_background) | |
} | |
phoneEditText.setOnEditorActionListener { v, actionId, event -> | |
return@setOnEditorActionListener when (actionId) { | |
EditorInfo.IME_ACTION_DONE -> { | |
hideKeyboard() | |
true | |
} | |
else -> false | |
} | |
} | |
loginButton.isClickable = true | |
loginButton.setOnClickListener { | |
viewModel.loginClicked( | |
formatWatcher.mask.filled(), | |
formatWatcher.mask.toUnformattedString(), | |
passwordField.text.toString(), | |
) | |
} | |
registerButton.setOnClickListener { | |
viewModel.registerClicked() | |
} | |
remeberPasswordsButton.setOnClickListener { | |
viewModel.navigateTo(PassChangeFragment.newInstance()) | |
} | |
} | |
} | |
override fun bindViewModelObservers() { | |
Log.d(TAG, "bindViewModelObservers:") | |
lifecycleScope.launch { | |
viewModel.viewStates() | |
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) | |
.distinctUntilChanged() | |
.collect { | |
Log.d(TAG, "viewStates: ${it?.javaClass?.name}") | |
when (it) { | |
is ProgressState -> { | |
showProgress(it.showProgress) | |
} | |
is ValidationErrorState -> { | |
showProgress(false) | |
handleError(it) | |
} | |
is SuccessState<*> -> { | |
showProgress(false) | |
} | |
is ErrorState -> { | |
showProgress(false) | |
} | |
} | |
} | |
} | |
lifecycleScope.launch { | |
viewModel.oneTimeActions() | |
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) | |
.distinctUntilChanged() | |
.collect { | |
Log.d(TAG, "oneTimeActions: ${it?.javaClass?.name}") | |
when (it) { | |
is ValidationErrorState -> { | |
showDefaultDialog( | |
title = getString(R.string.error_dialog_title), | |
message = it.errors.toMessage(), | |
case = InfoDialog.UseCase.Error | |
) | |
} | |
is ErrorState -> { | |
showErrorForState(it) | |
} | |
} | |
} | |
} | |
} | |
private fun handleError(errorState: ValidationErrorState) { | |
binding?.apply { | |
errorState.errors.entries.forEach { | |
when (it.key) { | |
ValidationError.IncorrectPhone -> { | |
phoneEditText.background = drawableCompat(R.drawable.edit_error_background) | |
} | |
ValidationError.IncorrectPass -> { | |
passwordField.background = drawableCompat(R.drawable.edit_error_background) | |
} | |
else -> { | |
} | |
} | |
} | |
} | |
} | |
override fun showProgress(show: Boolean) { | |
binding?.progressView?.isVisible = show | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@HiltViewModel | |
class LoginViewModel @Inject constructor( | |
private val userRepository: UserRepository, | |
router: Router, | |
application: Application | |
) : BaseFlowViewModel(router, application) { | |
private val _viewStates: MutableStateFlow<ViewModelState?> = MutableStateFlow(null) | |
fun viewStates(): StateFlow<ViewModelState?> = _viewStates | |
protected var viewState: ViewModelState | |
get() = _viewStates.value | |
?: throw UninitializedPropertyAccessException("\"viewState\" was queried before being initialized") | |
set(value) { | |
_viewStates.value = value | |
} | |
private val _oneTimeActions: MutableStateFlow<ViewModelState?> = MutableStateFlow(null) | |
fun oneTimeActions(): StateFlow<ViewModelState?> = _oneTimeActions | |
protected var oneTimeAction: ViewModelState | |
get() = _oneTimeActions.value | |
?: throw UninitializedPropertyAccessException("\"viewState\" was queried before being initialized") | |
set(value) { | |
_oneTimeActions.value = value | |
} | |
fun loginClicked(isFilled: Boolean, phone: String, pass: String) { | |
val errorsMap = hashMapOf<ValidationError, String>() | |
if (pass.isNullOrBlank()) { | |
errorsMap[ValidationError.IncorrectPass] = getString(R.string.empty_password) | |
} | |
if (!isFilled) { | |
errorsMap[ValidationError.IncorrectPhone] = getString(R.string.incorrect_phone) | |
} | |
viewModelScope.launch(Dispatchers.IO) { | |
if (errorsMap.isEmpty()) { | |
viewState = ProgressState(true) | |
try { | |
val res = userRepository.login(phone.filter { it.isDigit() }, pass)//suspend fun | |
viewState = SuccessState(res) | |
navigateToAndClearHistory(MainHostFragment()) | |
} catch (ex: Exception) { | |
viewState = ErrorState(dialogError = makeDialogError(ex)) | |
} | |
} else { | |
viewState = ValidationErrorState(errorsMap)//стейт ошибки | |
oneTimeAction = ValidationErrorState(errorsMap)//показ диалога | |
} | |
} | |
} | |
fun registerClicked() { | |
navigateTo(FirstStepFragment()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment