0Pricing
Kotlin Academy · Leçon

StateFlow : conteneur d’état actif pour l’interface utilisateur

Utilisez StateFlow comme conteneur d’état réactif et observez-le dans les ViewModels.

StateFlow : conteneur d’état actif pour l’interface utilisateur est une leçon Kotlin Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Kotlin Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Kotlin Academy comprend 4 leçons au total.

Qu'est-ce que StateFlow ?

StateFlow est un flux chaud qui conserve un état, possède toujours une valeur et émet les mises à jour vers tous les collecteurs. Il remplace LiveData dans les architectures Kotlin modernes.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val stateFlow = MutableStateFlow(0)  // initial value
    println("Current: ${stateFlow.value}")
    stateFlow.value = 42
    println("Updated: ${stateFlow.value}")
}

MutableStateFlow et StateFlow

MutableStateFlow est l'implémentation mutable utilisée en interne. Exposez StateFlow (en lecture seule) aux observateurs externes en effectuant une conversion ou en utilisant .asStateFlow().

import kotlinx.coroutines.flow.*
class CounterViewModel {
    private val _count = MutableStateFlow(0)  // mutable internally
    val count: StateFlow<Int> = _count.asStateFlow()  // read-only externally
    fun increment() { _count.value++ }
    fun decrement() { _count.value-- }
}

Collecte de StateFlow

Collectez StateFlow comme n'importe quel autre flux. La dernière valeur est émise immédiatement lors de la collecte.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val state = MutableStateFlow("loading")
    launch {
        state.collect { println("State: $it") }
    }
    delay(50); state.value = "success"
    delay(50); state.value = "idle"
    delay(50)
    coroutineContext.cancelChildren()
}

StateFlow regroupe les valeurs identiques

StateFlow n'émet que lorsque la valeur change. Si vous définissez deux fois la même valeur, une seule émission a lieu : les valeurs consécutives identiques sont regroupées.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableStateFlow("A")
    launch {
        flow.collect { println("Received: $it") }
    }
    delay(50)
    flow.value = "A"  // no emission — same value
    flow.value = "B"  // emits
    flow.value = "B"  // no emission — same value
    flow.value = "C"  // emits
    delay(50)
    coroutineContext.cancelChildren()
}

StateFlow dans ViewModel

Le modèle de référence avec ViewModel : un MutableStateFlow privé, modifié par la logique métier, et un StateFlow public, observé par l'interface utilisateur.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class UiState { object Loading : UiState(); data class Success(val data: String) : UiState(); data class Error(val msg: String) : UiState() }
class MyViewModel(scope: CoroutineScope) {
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    init {
        scope.launch {
            delay(100)  // simulate load
            _uiState.value = UiState.Success("Hello!")
        }
    }
}

Mise à jour atomique

Utilisez update { } pour mettre à jour l'état de manière atomique à partir de sa valeur actuelle, ce qui est important en cas de mises à jour concurrentes.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val counter = MutableStateFlow(0)
    List(100) {
        launch(Dispatchers.Default) {
            counter.update { it + 1 }  // thread-safe atomic update
        }
    }.forEach { it.join() }
    println("Counter: ${counter.value}")  // 100
}

compareAndSet pour les mises à jour optimistes

compareAndSet(expect, update) ne définit la nouvelle valeur que si la valeur actuelle correspond à expect. C'est utile pour la concurrence optimiste.

import kotlinx.coroutines.flow.*
fun main() {
    val state = MutableStateFlow("idle")
    val changed = state.compareAndSet("idle", "loading")
    println("Changed: $changed, Value: ${state.value}")  // true, loading
    val changed2 = state.compareAndSet("idle", "error")  // won't change
    println("Changed: $changed2, Value: ${state.value}") // false, loading
}

StateFlow et LiveData

StateFlow fonctionne sans cycle de vie Android. LiveData tient compte du cycle de vie, mais est limité à Android. Préférez StateFlow pour les ViewModels KMP partagés.

import kotlinx.coroutines.flow.*
// StateFlow: pure Kotlin, works in commonMain
// val state = MutableStateFlow("value")

// LiveData: Android-only, lifecycle-aware
// val liveData = MutableLiveData("value")

// In Compose, collect StateFlow with:
// val state by viewModel.uiState.collectAsState()
fun main() { println("StateFlow: multiplatform; LiveData: Android-only") }

stateIn : convertir un flux froid en StateFlow

flow.stateIn(scope, started, initialValue) convertit un flux froid en StateFlow chaud, en partageant un seul abonnement entre tous les collecteurs.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val cold = flow {
        println("Fetching...")
        delay(100)
        emit("data")
    }
    val hot: StateFlow<String> = cold.stateIn(
        scope = this,
        started = SharingStarted.Lazily,
        initialValue = "loading"
    )
    launch { hot.collect { println("A: $it") } }
    launch { hot.collect { println("B: $it") } }
    delay(200)
    coroutineContext.cancelChildren()
}

Stratégies de partage

Avec empressement : démarre immédiatement. Avec paresse : démarre avec le premier collecteur. WhileSubscribed(stopTimeout) : s'arrête lorsqu'il n'y a plus de collecteurs et redémarre lorsqu'un collecteur s'abonne.

import kotlinx.coroutines.flow.*
// Eagerly: upstream starts right away
// SharingStarted.Eagerly

// Lazily: waits for first subscriber
// SharingStarted.Lazily

// WhileSubscribed: stops 5s after last subscriber
// SharingStarted.WhileSubscribed(5000)

// Typical ViewModel usage:
// val uiState = repo.dataFlow.stateIn(viewModelScope, WhileSubscribed(5000), Loading)

Observer un état dérivé

Dérivez un nouveau StateFlow à partir d'un flux existant avec map + stateIn. Le flux dérivé est mis à jour chaque fois que la source change.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val count = MutableStateFlow(5)
    val doubled = count.map { it * 2 }.stateIn(this, SharingStarted.Eagerly, 10)
    println(doubled.value)  // 10
    count.value = 7
    delay(50)
    println(doubled.value)  // 14
    coroutineContext.cancelChildren()
}

Vérification rapide

Que fait StateFlow lorsque vous définissez deux fois la même valeur ?

Récapitulatif

StateFlow est un flux chaud, toujours doté d'une valeur, qui regroupe les valeurs identiques. Utilisez MutableStateFlow en interne et exposez StateFlow en externe. Utilisez la mise à jour atomique pour les modifications concurrentes et stateIn() pour convertir les flux froids.

Questions Fréquemment Posées

La leçon « StateFlow : conteneur d’état actif pour l’interface utilisateur » est-elle gratuite ?

Oui — le texte complet de « StateFlow : conteneur d’état actif pour l’interface utilisateur » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Kotlin Academy, passe à CoddyKit PRO. Le cours Kotlin Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « StateFlow : conteneur d’état actif pour l’interface utilisateur » ?

Utilisez StateFlow comme conteneur d’état réactif et observez-le dans les ViewModels. Tu pratiques Kotlin Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Kotlin Academy ?

Aucune expérience préalable n'est requise. Kotlin Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « StateFlow : conteneur d’état actif pour l’interface utilisateur » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Kotlin Academy ?

Oui. Chaque leçon Kotlin Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. StateFlow : conteneur d’état actif pour l’interface utilisateur
  2. SharedFlow : bus d’événements et événements ponctuels
  3. Transformer un Flow froid en flux actif avec shareIn et stateIn
  4. Tester StateFlow et SharedFlow avec Turbine
← Retour à Kotlin Academy