Les Flows sont un concept central dans le développement Android moderne avec Kotlin. Ils permettent de gérer les flux de données de manière réactive et sont particulièrement bien adaptés pour l’architecture MVVM avec Compose.
Un Flow est un flux de données asynchrone qui peut émettre plusieurs valeurs séquentiellement. C’est l’équivalent Kotlin des Observables en RxJava ou des Streams en Java.
// Flow basique
val simpleFlow = flow {
emit(1)
delay(500)
emit(2)
delay(500)
emit(3)
}
// StateFlow
private val _counter = MutableStateFlow(0)
val counter: StateFlow<Int> = _counter.asStateFlow()
| Flow | StateFlow |
|---|---|
| Peut émettre plusieurs valeurs | Maintient toujours une valeur courante |
| Ne stocke pas de valeur | Stocke la dernière valeur émise |
| Démarre à chaque collection | Hot stream (toujours actif) |
| Idéal pour les données éphémères | Parfait pour l’état de l’UI |
Les ViewModels utilisent généralement StateFlow pour l’état de l’UI :
class OrderViewModel : ViewModel() {
// UI State
private val _uiState = MutableStateFlow<OrderUiState>(OrderUiState.Initial)
val uiState: StateFlow<OrderUiState> = _uiState.asStateFlow()
// Events (utilisation d'un SharedFlow pour les événements ponctuels)
private val _events = MutableSharedFlow<OrderEvent>()
val events = _events.asSharedFlow()
fun loadOrders() {
viewModelScope.launch {
_uiState.value = OrderUiState.Loading
try {
val orders = repository.getOrders()
_uiState.value = OrderUiState.Success(orders)
} catch (e: Exception) {
_uiState.value = OrderUiState.Error(e.message)
}
}
}
}
sealed class OrderUiState {
data object Initial : OrderUiState()
data object Loading : OrderUiState()
data class Success(val orders: List<Order>) : OrderUiState()
data class Error(val message: String?) : OrderUiState()
}
Compose fournit des extensions pratiques pour collecter les Flows :
@Composable
fun OrderScreen(viewModel: OrderViewModel) {
// Collecte du StateFlow
val uiState by viewModel.uiState.collectAsState()
// Gestion des différents états
when (uiState) {
is OrderUiState.Initial -> {
// Afficher l'état initial
}
is OrderUiState.Loading -> {
LoadingIndicator()
}
is OrderUiState.Success -> {
val orders = (uiState as OrderUiState.Success).orders
OrderList(orders = orders)
}
is OrderUiState.Error -> {
val message = (uiState as OrderUiState.Error).message
ErrorMessage(message = message)
}
}
}
StateFlow pour l’UI State
SharedFlow pour les Events
Flow pour les données asynchrones
Gestion des erreurs
// Exemple d'utilisation combinée
class OrderViewModel : ViewModel() {
private val _uiState = MutableStateFlow<OrderUiState>(OrderUiState.Initial)
val uiState = _uiState.asStateFlow()
init {
// Transformation d'un Flow en StateFlow
repository.getOrdersFlow()
.map { OrderUiState.Success(it) }
.catch { emit(OrderUiState.Error(it.message)) }
.onStart { emit(OrderUiState.Loading) }
.onEach { _uiState.value = it }
.launchIn(viewModelScope)
}
}
StateFlow est le successeur moderne de LiveData:
Les Flows supportent de nombreux opérateurs :
map, filter, transformcombine, merge, zipcatch, retry, onEach// Exemple d'opérateurs Flow
repository.getOrdersFlow()
.map { orders -> orders.filter { it.isActive } }
.catch { /* gestion d'erreur */ }
.collect { /* utilisation des données */ }
đź’ˇ Astuce
L’opérateur combine ne fonctionne que si les flows ont émis au moins une valeur !
flowOf pour des flow asynchrones qui vont émettre une seule valeur.Les Flows sont testables grâce à TestCoroutineDispatcher :
@Test
fun `test flow emissions`() = runTest {
val viewModel = OrderViewModel(testRepository)
viewModel.loadOrders()
val states = mutableListOf<OrderUiState>()
val job = launch { viewModel.uiState.toList(states) }
assertEquals(OrderUiState.Loading, states[0])
assertEquals(OrderUiState.Success(testOrders), states[1])
job.cancel()
}
| Critère | Validation |
|---|---|
| Vous comprenez la différence entre Flow et StateFlow | [ ] |
| Vous savez utiliser StateFlow dans un ViewModel | [ ] |
| Vous savez collecter un Flow dans un Composable | [ ] |
| Vous maîtrisez la gestion des états avec Flow | [ ] |
| Vous comprenez les opérateurs de base des Flows | [ ] |
đź’ˇ Conseil
Les Flows sont un outil puissant mais peuvent sembler complexes au début. Commencez par les cas simples avec StateFlow dans vos ViewModels, puis explorez progressivement les fonctionnalités plus avancées.