Como lidar na arquitetura MVI com eventos que não precisam ser salvos?
sobes.tech IA
Resposta da IA
Para lidar com eventos únicos (por exemplo, mostrar uma toast, navegação) no MVI, que não devem sobreviver à recriação da View nem serem armazenados no estado, utilizam-se diferentes abordagens:
-
Efeitos Colaterais (SingleLiveEvent / Channels):
Mecanismo especial para enviar eventos do ViewModel para a View.
SingleLiveEvent(em projetos antigos ou bibliotecas comoandroidx.lifecycle:lifecycle-livedata-ktx), ouChanneldo Flow (em projetos modernos). Garantem que o evento seja consumido apenas uma vez.// ViewModel com Flow e Channel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch class MyViewModel : ViewModel() { private val _sideEffect = Channel<SideEffect>(Channel.BUFFERED) val sideEffect = _sideEffect.receiveAsFlow() fun doSomething() { // Lógica de negócio... viewModelScope.launch { _sideEffect.send(SideEffect.ShowToast("Operação bem-sucedida!")) } } } sealed class SideEffect { data class ShowToast(val message: String) : SideEffect() object NavigateNext : SideEffect() } // Na View (Fragmento/Atividade), observando SideEffect import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class MyFragment : Fragment() { private val viewModel: MyViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { viewModel.sideEffect.collect { effect -> when (effect) { is SideEffect.ShowToast -> showToast(effect.message) SideEffect.NavigateNext -> navigateToNextScreen() } } } } private fun showToast(message: String) { // Implementação para mostrar toast } private fun navigateToNextScreen() { // Implementação para navegação } } -
Eventos de uso único no Estado:
Menos recomendado, mas às vezes usado em casos simples. Adiciona-se uma flag ou campo no próprio estado
UiStateque indica a presença de um evento de uso único. Após o processamento do evento na View, a View envia um novoIntentao ViewModel para resetar essa flag ou campo no estado. Isso pode complicar a lógica e potencialmente levar à reativação em caso de má gestão ou recriação da View.// Exemplo de Estado com flag de uso único data class MyUiState( val data: List<Item> = emptyList(), val isLoading: Boolean = false, val showSuccessToast: Boolean = false // Flag para evento de uso único ) // No ViewModel, após operação bem-sucedida fun processSuccess() { _uiState.update { it.copy(showSuccessToast = true) } } // Na View, ao processar showSuccessToast = true if (state.showSuccessToast) { showToast("Sucesso!") // Importante: enviar um Intent para resetar a flag viewModel.onToastShown() } // No ViewModel, para resetar a flag fun onToastShown() { _uiState.update { it.copy(showSuccessToast = false) } }
A abordagem mais recomendada no Android moderno com Flow e MVI é usar Efeitos Colaterais via Channel. É uma solução limpa, segura e previsível para eventos de uso único.