Sobes.tech
Middle+

Comment gérer dans l'architecture MVI les événements qui ne doivent pas être enregistrés ?

sobes.tech IA

Réponse de l'IA

Pour gérer des événements uniques (par exemple, afficher une toast, navigation) dans MVI, qui ne doivent pas survivre à la recréation de la vue ni être stockés dans l'état, différentes approches sont utilisées:

  1. Effets secondaires (SingleLiveEvent / Channels):

    Mécanisme spécial pour envoyer des événements du ViewModel à la vue. SingleLiveEvent (dans d'anciens projets ou bibliothèques comme androidx.lifecycle:lifecycle-livedata-ktx), ou Channel de Flow (dans les projets modernes). Ils garantissent que l'événement ne sera consommé qu'une seule fois.

    // ViewModel avec Flow et 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() {
            // Logique métier...
            viewModelScope.launch {
                _sideEffect.send(SideEffect.ShowToast("Opération réussie !"))
            }
        }
    }
    
    sealed class SideEffect {
        data class ShowToast(val message: String) : SideEffect()
        object NavigateNext : SideEffect()
    }
    
    // Dans la vue (Fragment/Activity), observation de 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) {
            // Implémentation pour afficher la toast
        }
    
        private fun navigateToNextScreen() {
            // Implémentation pour la navigation
        }
    }
    
  2. Événements à usage unique dans l'état:

    Option moins recommandée, mais parfois utilisée dans des cas simples. Ajoutez un drapeau ou un champ dans l'état UiState lui-même, indiquant la présence d'un événement à usage unique. Après traitement de l'événement dans la vue, la vue envoie un nouvel Intent au ViewModel pour réinitialiser ce drapeau ou ce champ dans l'état. Cela peut compliquer la logique et potentiellement entraîner une réactivation en cas de mauvaise gestion ou de recréation de la vue.

    // Exemple d'état avec drapeau à usage unique
    data class MyUiState(
        val data: List<Item> = emptyList(),
        val isLoading: Boolean = false,
        val showSuccessToast: Boolean = false // Drapeau pour événement à usage unique
    )
    
    // Dans le ViewModel, après une opération réussie
    fun processSuccess() {
        _uiState.update { it.copy(showSuccessToast = true) }
    }
    
    // Dans la vue, lors du traitement de showSuccessToast = true
    if (state.showSuccessToast) {
        showToast("Succès !")
        // Important : envoyer un Intent pour réinitialiser le drapeau
        viewModel.onToastShown()
    }
    
    // Dans le ViewModel, pour réinitialiser le drapeau
    fun onToastShown() {
        _uiState.update { it.copy(showSuccessToast = false) }
    }
    

L'approche la plus recommandée dans Android moderne avec Flow et MVI est d'utiliser Effets secondaires via Channel. C'est une solution propre, sûre et prévisible pour les événements à usage unique.