Flow và StateFlow
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Flow là cold asynchronous stream của Kotlin, tương tự RxJava Observable nhưng đơn giản hơn.
2. Flow cơ bản
Phần tiêu đề “2. Flow cơ bản”Tạo Flow
Phần tiêu đề “Tạo Flow”// flow builderval numbersFlow: Flow<Int> = flow { for (i in 1..5) { delay(100) emit(i) }}
// flowOfval simpleFlow = flowOf(1, 2, 3)
// asFlowval listFlow = listOf(1, 2, 3).asFlow()Collect Flow
Phần tiêu đề “Collect Flow”viewModelScope.launch { numbersFlow.collect { value -> println(value) }}3. Flow Operators
Phần tiêu đề “3. Flow Operators”val flow = flowOf(1, 2, 3, 4, 5)
// mapflow.map { it * 2 } // 2, 4, 6, 8, 10
// filterflow.filter { it % 2 == 0 } // 2, 4
// takeflow.take(3) // 1, 2, 3
// debounce (useful for search)searchQueryFlow .debounce(300) .distinctUntilChanged() .flatMapLatest { query -> search(query) }
// combinecombine(flow1, flow2) { a, b -> a + b}
// zipflow1.zip(flow2) { a, b -> Pair(a, b)}4. StateFlow
Phần tiêu đề “4. StateFlow”Cold vs Hot:
- Flow: Cold - emit khi có collector
- StateFlow: Hot - luôn có giá trị hiện tại
class CounterViewModel : ViewModel() { // MutableStateFlow cho internal use private val _count = MutableStateFlow(0)
// StateFlow cho external use (read-only) val count: StateFlow<Int> = _count.asStateFlow()
fun increment() { _count.value++ // hoặc _count.update { it + 1 } }}5. SharedFlow
Phần tiêu đề “5. SharedFlow”Dùng cho events (không có initial value):
class EventViewModel : ViewModel() { private val _events = MutableSharedFlow<UiEvent>() val events: SharedFlow<UiEvent> = _events.asSharedFlow()
fun showSnackbar(message: String) { viewModelScope.launch { _events.emit(UiEvent.ShowSnackbar(message)) } }}
sealed class UiEvent { data class ShowSnackbar(val message: String) : UiEvent() object NavigateBack : UiEvent()}6. StateFlow vs LiveData
Phần tiêu đề “6. StateFlow vs LiveData”| Feature | StateFlow | LiveData |
|---|---|---|
| Initial value | Required | Optional |
| Nullability | Nullable | Nullable |
| Lifecycle aware | Manual | Auto |
| Operators | Rich | Limited |
| Testing | Easy | Needs testutils |
7. Collect trong Compose
Phần tiêu đề “7. Collect trong Compose”@Composablefun CounterScreen(viewModel: CounterViewModel = viewModel()) { // collectAsState cho StateFlow val count by viewModel.count.collectAsState()
Column { Text("Count: $count") Button(onClick = { viewModel.increment() }) { Text("Increment") } }}Collect với Lifecycle
Phần tiêu đề “Collect với Lifecycle”@Composablefun EventHandler(viewModel: EventViewModel) { val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) { viewModel.events.collect { event -> when (event) { is UiEvent.ShowSnackbar -> { snackbarHostState.showSnackbar(event.message) } is UiEvent.NavigateBack -> { // handle } } } }}8. Flow to StateFlow
Phần tiêu đề “8. Flow to StateFlow”class UserViewModel(private val repository: UserRepository) : ViewModel() {
val users: StateFlow<List<User>> = repository.getUsersFlow() .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList() )}SharingStarted options
Phần tiêu đề “SharingStarted options”// Lazily: Bắt đầu khi có subscriber đầu tiênSharingStarted.Lazily
// Eagerly: Bắt đầu ngay lập tứcSharingStarted.Eagerly
// WhileSubscribed: Active khi có subscribersSharingStarted.WhileSubscribed( stopTimeoutMillis = 5000, replayExpirationMillis = Long.MAX_VALUE)9. Room with Flow
Phần tiêu đề “9. Room with Flow”@Daointerface UserDao { @Query("SELECT * FROM users") fun getAllUsers(): Flow<List<User>>}
class UserRepository(private val userDao: UserDao) { fun getUsers(): Flow<List<User>> = userDao.getAllUsers()}10. Error Handling
Phần tiêu đề “10. Error Handling”repository.getDataFlow() .catch { exception -> emit(Result.Error(exception)) } .onEach { data -> _uiState.value = UiState.Success(data) } .launchIn(viewModelScope)11. UI State Pattern
Phần tiêu đề “11. UI State Pattern”data class HomeUiState( val isLoading: Boolean = false, val users: List<User> = emptyList(), val error: String? = null)
class HomeViewModel(private val repository: UserRepository) : ViewModel() {
private val _uiState = MutableStateFlow(HomeUiState()) val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
init { loadUsers() }
private fun loadUsers() { viewModelScope.launch { _uiState.update { it.copy(isLoading = true) }
repository.getUsers() .onSuccess { users -> _uiState.update { it.copy(isLoading = false, users = users) } } .onFailure { error -> _uiState.update { it.copy(isLoading = false, error = error.message) } } } }}📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Type | Use Case |
|---|---|
| Flow | Cold stream, database queries |
| StateFlow | UI state, always has value |
| SharedFlow | Events, one-time actions |
| Operator | Mô tả |
|---|---|
| map | Transform items |
| filter | Filter items |
| collect | Consume flow |
| stateIn | Convert to StateFlow |
| combine | Merge multiple flows |