Blog Infos
Author
Published
Topics
, , ,
Published
Who’s Next?!

Who’s Next!?

Finite State Machine (FSM)
sealed class State {
object Idle : State()
object SettingTimer : State()
object CountingDown : State()
object Paused : State()
object Restarting : State()
}
view raw State.kt hosted with ❤ by GitHub

FSM states

 

sealed class Event {
object OnSetTimer : Event()
class OnTimerSet(val value: Int) : Event()
object OnStart : Event()
object OnFinish : Event()
object OnPause : Event()
object OnStop : Event()
object OnReset : Event()
}
view raw Events.kt hosted with ❤ by GitHub

FSM events

 

Who’s Next FSM

Finally, our state machine can also produce SideEffects when transitioning between states:

sealed class SideEffect {
object SetTimer : SideEffect()
class TimerReady(val value: Int) : SideEffect()
object StartCountDown : SideEffect()
object Restarting : SideEffect()
object Pause : SideEffect()
object Stop : SideEffect()
object Reset : SideEffect()
}
view raw SideEffect.kt hosted with ❤ by GitHub

FSM side effects

 

Implementation
val stateMachine = StateMachine.create<State, Event, SideEffect> {
initialState(State)
state<State> {
on<Event> {
transitionTo(State, SideEffect)
}
//more events
}
//more states
onTransition {
//process side effects
}
}
view raw FSM.kt hosted with ❤ by GitHub

DSL

 

Let’s recall our diagram and write the FSM code to alternate between both Idle and SettingTimer states:

FROM -> TO by [EVENT]
Idle -> SettingTimer by [OnSetTimer]
SettingTimer -> Idle by [OnTimerSet]
StateMachine.create<TimerState.State, TimerState.Event, TimerState.SideEffect> {
initialState(TimerState.State.Idle)
state<TimerState.State.Idle> {
on<TimerState.Event.OnSetTimer> {
transitionTo(TimerState.State.SettingTimer, TimerState.SideEffect.SetTimer)
}
}
state<TimerState.State.SettingTimer> {
on<TimerState.Event.OnTimerSet> {
transitionTo(TimerState.State.Idle, TimerState.SideEffect.TimerReady(it.value))
}
}
onTransition {
val validTransition = it as? StateMachine.Transition.Valid ?: return@onTransition
//emit ui state changes
}
}
view raw FSM.kt hosted with ❤ by GitHub

Idle and SettingTimer configuration

 

And the result is:

FSM operations output

 

Job Offers

Job Offers


    Mobile Engineer

    OLX Group
    Remote, Portugal, Spain, Romania, Poland
    • Full Time
    apply now

    Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now

    Kotlin Multiplatform Mobile Developer

    Touchlab
    Remote
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

,

Overview of UI testing in Compose

Jetpack Compose has no views, so how do we test it? Come learn about the basics of UI testing in Jetpack Compose.
Watch Video

Overview of UI testing in Compose

Michael Bailey
Distinguished Engineer
American Express

Overview of UI testing in Compose

Michael Bailey
Distinguished Engine ...
American Express

Overview of UI testing in Compose

Michael Bailey
Distinguished Engineer
American Express

Jobs

Model-View-Intent (MVI)
Implementation
data class TimerUiState(
val value: Int = 0, //in seconds
val progress: Float = 0f, //0-100
val isSettingTimer: Boolean = false,
val isCountingDown: Boolean = false,
val isRestarting: Boolean = false
)
view raw TimerUiState.kt hosted with ❤ by GitHub

MVI State

 

class TimerViewModel : ViewModel() {
private val _state = MutableLiveData<TimerUiState>(TimerUiState())
val state: LiveData<TimerUiState> = _state
fun settingTime() {
_state.value = state.value.copy(isSettingTimer = true, isCountingDown = false, isRestarting = false)
}
fun setTime(seconds: Int) {
_state.value = TimerUiState(seconds)
}
}
//Observing state changes in the View
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
with(viewModel.state.observeAsState().value) { /*...*/ }
}

MVVM implementation

 

With Orbit Multiplatform we change it to:

class TimerViewModel : ViewModel(), ContainerHost<TimerUiState, Nothing> {
override val container = viewModelScope.container<TimerUiState, Nothing>(TimerUiState())
fun settingTime() {
intent { reduce { state.copy(isSettingTimer = true, isCountingDown = false, isRestarting = false) } }
}
fun setTime(seconds: Int) {
intent { reduce { TimerUiState(seconds) } }
}
}
//Collecting state changes in the View
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
with(viewModel.collectAsState().value) { /*...*/ }
}

MVI implementation

 

class TimerViewModel : ViewModel(), ContainerHost<TimerUiState, Nothing> {
override val container = viewModelScope.container<TimerUiState, Nothing>(TimerUiState())
private val stateMachine = StateMachine.create<TimerState.State, TimerState.Event, TimerState.SideEffect> {
initialState(TimerState.State.Idle)
state<TimerState.State.Idle> {
on<TimerState.Event.OnSetTimer> {
transitionTo(TimerState.State.SettingTimer, TimerState.SideEffect.SetTimer)
}
}
state<TimerState.State.SettingTimer> {
on<TimerState.Event.OnTimerSet> {
transitionTo(TimerState.State.Idle, TimerState.SideEffect.TimerReady(it.value))
}
}
onTransition {
val validTransition = it as? StateMachine.Transition.Valid ?: return@onTransition
when (validTransition.sideEffect as TimerState.SideEffect) {
is TimerState.SideEffect.SetTimer -> intent { reduce { state.copy(isSettingTimer = true, isCountingDown = false, isRestarting = false) } }
is TimerState.SideEffect.TimerReady -> intent { reduce { TimerUiState(effect.value) } }
}
}
}
fun settingTime() {
stateMachine.transition(TimerState.Event.OnSetTimer)
}
fun setTime(seconds: Int) {
stateMachine.transition(TimerState.Event.OnTimerSet(seconds))
}
}
//Collecting state changes in the View
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
with(viewModel.collectAsState().value) { /*...*/ }
}

FSM (state: Idle and SettingTimer) + MVI implementation

 

FSM+MVI

Conclusions

Who’s Next!?

Final thoughts

Thanks to Matthew Dolan.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
In this part of the series, we will plan our first screen in Jetpack…
READ MORE
blog
We’ll be selecting a time whenever a user presses a number key. Following points…
READ MORE
blog
Ask yourself a fairly standard question for any interview to fill an Android position:…
READ MORE
blog
This is part of a multi-part series about learning to use Jetpack Compose through…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu