Created
July 23, 2025 14:48
-
-
Save albertklik/6fec818dcb2a1258d8a1a6b4689573a4 to your computer and use it in GitHub Desktop.
Update UI State Extention with state builder for enhance MVI architecture
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
| /** | |
| * A contract for a Builder class that can build a final object of type T. | |
| */ | |
| interface Builder<T> { | |
| fun build(): T | |
| } | |
| /** | |
| * A contract for a State class that can provide a Builder for itself. | |
| */ | |
| interface Buildable<B : Builder<out Any>> { | |
| fun toBuilder(): B | |
| } | |
| /** | |
| * Atomically and generically updates a MutableStateFlow. | |
| * | |
| * This function is thread-safe and works for any state class that | |
| * implements the `Buildable` and `Builder` interfaces. | |
| * | |
| * @param T The type of the state, which must be `Buildable`. | |
| * @param B The type of the builder, which must be `Builder<T>`. | |
| * @param block The receiver lambda in which the builder is modified. | |
| */ | |
| fun <T, B> MutableStateFlow<T>.updateWithBuilder( | |
| block: B.() -> Unit | |
| ) where T : Buildable<B>, B : Builder<T> { | |
| this.update { currentState -> | |
| // 1. Get the builder from the current state | |
| val builder = currentState.toBuilder() | |
| // 2. Apply the user's modifications to the builder | |
| builder.block() | |
| // 3. Build the new state and return it for the atomic update | |
| builder.build() | |
| } | |
| } |
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
| package [...] | |
| import [...] | |
| object ScreenContract { | |
| // The state data class now implements Buildable | |
| data class State( | |
| val loading: Boolean = false, | |
| val success: Boolean = false, | |
| val error: String? = null | |
| ): Buildable<State.StateBuilder> { | |
| // The implementation is simple: it just creates and returns an instance of its Builder | |
| override fun toBuilder(): StateBuilder = StateBuilder(this) | |
| // The Builder is a nested class that implements the Builder<State> interface | |
| class StateBuilder(state: State) : Builder<State> { | |
| var loading: Boolean = state.loading | |
| var success: Boolean = state.success | |
| var error: String? = state.error | |
| // The implementation builds and returns a new immutable State object | |
| override fun build(): State = State(loading, success, error) | |
| } | |
| } | |
| sealed interface Event { | |
| data object OnAppear : Event | |
| data object OnLoginClicked : Event | |
| data object OnPoliticaDePrivacidadeClicked : Event | |
| data class OnAccountsLoaded(val emails: List<String>) : Event | |
| } | |
| sealed interface Effect { | |
| data class ShowErrorSnackBar(val message: String) : Effect | |
| } | |
| } |
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
| class MyViewModel : ViewModel() { | |
| private val _state = MutableStateFlow(State()) | |
| val state: StateFlow<State> = _state | |
| private fun updateUI(block: ScreenContract.State.StateBuilder.() -> Unit) { | |
| _state.updateWithBuilder { it.toBuilder().apply(block).build() } | |
| } | |
| fun fetchData() { | |
| // The call remains clean, but is now generic and 100% thread-safe | |
| updateUI { | |
| loading = true | |
| error = null | |
| } | |
| viewModelScope.launch(Dispatchers.IO) { | |
| // ... fetching data on a background thread ... | |
| val result = "Data received" | |
| // Update the state from any thread with confidence | |
| updateUI { | |
| loading = false | |
| success = true | |
| // error = result // Just an example | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment