Trigger a Transition

Once you have a FSM you need to trigger a transition to a new state.

This is done with the StateMachine().applyEvent method.

var machine = StateMachine.create((g) => g
          ..state<Solid>((b) => b
            ..on<OnMelted, Liquid>(
                sideEffect: (e) => watcher.log(onMeltedMessage))
            ..onEnter((s, e) => watcher?.onEnter(s))
            ..onExit((s, e) => watcher?.onExit(s)))
          ..state<Liquid>((b) => b
            ..onEnter((s, e) => watcher?.onEnter(s))
            ..onExit((s, e) => watcher?.onExit(s))
            ..on<OnFroze, Solid>(
                sideEffect: (e) => watcher.log(onFrozenMessage))
            ..on<OnVaporized, Gas>(
                sideEffect: (e) => watcher.log(onVaporizedMessage)))
          ..state<Gas>((b) => b..on<OnCondensed, Liquid>(
              sideEffect: (e) => watcher.log(onCondensedMessage)))

The applyEvent method takes an Event and may cause the FSM to transition to a new state.

If you have concurrent regions or nested states then a single event may result in multiple transitions.

You should be aware that some calls to `applyEvent` will not result in a change in State. This can happen due to Guard Conditions suppressing the transition.

Calls to StateMachine.applyEvent are asynchronous.

Your state machine will not have finished transitioning when applyEvent returns! Use an onEnter, onExit, sideEffect or stream to get notifications of when the statemachine has entered a new state.

Calls to StateMachine.applyEvent can be made at any time and from any code.

The StateMachine will queue any events and only apply the event once any active or pre existing queued events have completed.

Invalid Transitions

If you call applyEvent with an event you must be in a state that has an handler for that event.

final machine = StateMachine.create((g) => g
    ..state<Solid>((b) => b
      ..on<OnMelted, Liquid>(sideEffect: (e) async => print('Melted'))
    ..state<Liquid>((b) => b
      ..on<OnFroze, Solid>(sideEffect: (e) async => print('Frozen'))
      ..on<OnVaporized, Gas>(sideEffect: (e) async => print('Vaporized')))
    ..state<Gas>((b) => b
      ..on<OnCondensed, Liquid>(sideEffect: (e) async => print('Condensed'))));


The above call to 'machine.applyEvent' will fail as the initialState is 'Solid' and the 'Solid' state doesn't have a handler for the 'OnFroze' event.

Note: FSM2 supports inherited transition handlers so an event can be processed if an active State OR one of its parents has a handler for the state.

If the statemachine is can't handle the event then a 'InvalidTransitionException' is thrown.

The exception is thrown only when in debug mode. If you pass 'production: true' to the statemachine 'create' method then the exception will be suppressed and instead it will be logged. This is done to make the FSM less brittle in production.

Concurrent Regions

If you have concurrent regions in you FSM and a concurrent region is active when an event is applied, only one of the active states needs to be able to handle the event. If at least on of the actives states handles the event then an 'InvalidTransitionException' will not be thrown.

Last updated