Created
August 11, 2025 19:20
-
-
Save mahdiPourkazemi/c38d319f8ed8774a3573872f39fc7d23 to your computer and use it in GitHub Desktop.
fiveFlowUseFullPattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| The One-Shot Operation as a Cold Stream | |
| //######################################################################## | |
| class MyViewModel(private val repository: DataRepository) : ViewModel() { | |
| private val _data = MutableStateFlow<UiState>(UiState.Loading) | |
| val data: StateFlow<UiState> = _data | |
| fun fetchData() { | |
| viewModelScope.launch { | |
| try { | |
| _data.value = UiState.Success(repository.fetchSomeData()) | |
| } catch (e: Exception) { | |
| _data.value = UiState.Error(e) | |
| } | |
| } | |
| } | |
| } | |
| //#############################into this####################################### | |
| class MyViewModel(private val repository: DataRepository) : ViewModel() { | |
| val data: StateFlow<UiState> = flow { | |
| // This block is the coroutine scope | |
| emit(repository.fetchSomeData()) | |
| } | |
| .map { result -> UiState.Success(result) } // Transform data to UI state | |
| .catch { e -> emit(UiState.Error(e)) } // Handle errors declaratively | |
| .stateIn( | |
| scope = viewModelScope, | |
| started = SharingStarted.WhileSubscribed(5000), | |
| initialValue = UiState.Loading | |
| ) | |
| } | |
| //######################################################################## | |
| Callbacks with callbackFlow | |
| fun Context.locationFlow(): Flow<Location> = callbackFlow { | |
| val locationListener = object : LocationListener { | |
| override fun onLocationChanged(location: Location) { | |
| // Offer the value to the flow's collector | |
| trySend(location).isSuccess | |
| } | |
| // Other listener methods... | |
| } | |
| val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager | |
| locationManager.requestLocationUpdates( | |
| LocationManager.GPS_PROVIDER, 5000, 10f, locationListener | |
| ) | |
| // This is the magic part | |
| awaitClose { | |
| // This block is executed when the flow is cancelled | |
| locationManager.removeUpdates(locationListener) | |
| } | |
| } | |
| //######################################################################## | |
| The Search Bar with debounce and flatMapLatest | |
| // In your ViewModel | |
| val searchQuery = MutableStateFlow("") | |
| val searchResults: Flow<List<Result>> = searchQuery | |
| .debounce(300) // Wait for user to stop typing for 300ms | |
| .filter { query -> query.length > 2 } // Only search if query is long enough | |
| .distinctUntilChanged() // Don't re-search for the same text | |
| .flatMapLatest { query -> // The key operator | |
| // Creates a new flow for the network call | |
| // AND cancels the previous one if a new query comes in | |
| api.search(query) | |
| } | |
| .catch { | |
| // Handle network errors for the search | |
| emit(emptyList()) | |
| } | |
| //######################################################################## | |
| // Define user actions as a sealed class | |
| sealed class LikeAction { | |
| object Increment : LikeAction() | |
| object Decrement : LikeAction() | |
| } | |
| val userActions = MutableSharedFlow<LikeAction>() // A flow of events | |
| val likeCount: StateFlow<Int> = userActions | |
| .scan(0) { currentCount, action -> // Start with 0, and process each action | |
| when (action) { | |
| is LikeAction.Increment -> currentCount + 1 | |
| is LikeAction.Decrement -> maxOf(0, currentCount - 1) | |
| } | |
| } | |
| .stateIn( | |
| scope = viewModelScope, | |
| started = SharingStarted.WhileSubscribed(5000), | |
| initialValue = 0 | |
| ) | |
| // From the UI, you would just call: | |
| // viewModel.userActions.tryEmit(LikeAction.Increment) | |
| viewModel.userActions.tryEmit(LikeAction.Increment) | |
| //##############################examole of ^\ ###################################### | |
| sealed class LikeAction { | |
| object Increment : LikeAction() | |
| object Decrement : LikeAction() | |
| object Undo : LikeAction() | |
| } | |
| data class LikeState(val count: Int, val history: List<Int>) | |
| val userActions = MutableSharedFlow<LikeAction>() | |
| val likeState: StateFlow<LikeState> = userActions | |
| .scan(LikeState(0, emptyList())) { currentState, action -> | |
| when (action) { | |
| is LikeAction.Increment -> | |
| currentState.copy( | |
| count = currentState.count + 1, | |
| history = currentState.history + currentState.count | |
| ) | |
| is LikeAction.Decrement -> | |
| currentState.copy( | |
| count = maxOf(0, currentState.count - 1), | |
| history = currentState.history + currentState.count | |
| ) | |
| is LikeAction.Undo -> | |
| if (currentState.history.isNotEmpty()) { | |
| val last = currentState.history.last() | |
| currentState.copy( | |
| count = last, | |
| history = currentState.history.dropLast(1) | |
| ) | |
| } else currentState | |
| } | |
| } | |
| .stateIn(viewModelScope, | |
| SharingStarted.WhileSubscribed(5000), | |
| LikeState(0, emptyList()) | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://trricho.medium.com/beyond-coroutines-5-flow-patterns-to-rethink-your-async-code-a1caa3a88c7e