state4d 0.0.1 copy "state4d: ^0.0.1" to clipboard
state4d: ^0.0.1 copied to clipboard

Simple state management for Dart

example/lib/main.dart

import 'dart:math';

import 'package:result4d/result4d.dart';
import 'package:state4d/state4d.dart';

// this is our entity - it tracks the state
class CupOfTea {
  final TeaState state;
  final String lastAction;

  CupOfTea(this.state, this.lastAction);

  CupOfTea copyWith({TeaState? state, String? lastAction}) =>
      CupOfTea(state ?? this.state, lastAction ?? this.lastAction);

  @override
  String toString() => 'CupOfTea(state: $state, lastAction: $lastAction)';
}

// the various states the entity can be in
enum TeaState {
  getCup,
  boilingWater,
  steepingTea,
  checkForMilk,
  whiteTea,
  blackTea,
}

// commands define actions which can result in dynamically generated events
enum TeaCommands { doYouHaveMilk }

// events transition the machine from one state to another
sealed class TeaEvent {}

class TurnOnKettle extends TeaEvent {}

class PourWater extends TeaEvent {}

class MilkPlease extends TeaEvent {}

class NoMilkPlease extends TeaEvent {}

class MilkIsFull extends TeaEvent {}

class MilkIsEmpty extends TeaEvent {}

// the lens gets and sets the state on the Entity
final lens = StateIdLens<CupOfTea, TeaState>(
  (entity) => entity.state,
  (entity, state) => entity.copyWith(state: state),
);

// commands is responsible for issuing new orders which will generate new events
Result<void, String> commands(CupOfTea entity, TeaCommands command) {
  print('Issuing command $command for $entity');
  return Success(null);
}

// define the machine - only Event type needed, rest inferred
final teaStateMachine = StateMachine.create(commands, lens).states<TeaEvent>(
  (state) => [
    // the state transitions for GetCup - we don't need to update the entity
    state(TeaState.getCup).transition(TurnOnKettle, TeaState.boilingWater),

    // the state transitions for BoilingWater - we can update the entity
    state(TeaState.boilingWater).transition(
      PourWater,
      TeaState.steepingTea,
      (event, entity) => entity.copyWith(lastAction: 'Waiting...'),
    ),

    // when we enter SteepingTea, we ask if they have milk (a command). The result of that
    // command will be a MilkPlease or NoMilkPlease event
    state(TeaState.steepingTea)
        .onEnter(TeaCommands.doYouHaveMilk)
        .transition(MilkPlease, TeaState.checkForMilk)
        .transition(NoMilkPlease, TeaState.blackTea),

    state(TeaState.checkForMilk)
        .transition(MilkIsFull, TeaState.whiteTea)
        .transition(MilkIsEmpty, TeaState.blackTea),

    state(TeaState.blackTea),
  ],
);

// this is the type of the result of a transition
typedef TeaResult =
    Result<
      StateTransitionResult<TeaState, CupOfTea, TeaEvent, TeaCommands>,
      String
    >;

void main() async {
  // returns OK with the updated entity - state only,
  final TeaResult boilingKettle = await teaStateMachine.transition(
    CupOfTea(TeaState.getCup, '-'),
    TurnOnKettle(),
  );

  final updatedCupOfTea = (boilingKettle as Success).value.entity;
  print(updatedCupOfTea);

  // returns OK with the updated entity - the lastAction is updated
  final TeaResult steepingTea = await teaStateMachine.transition(
    updatedCupOfTea,
    PourWater(),
  );

  final updatedCupOfTea2 = (steepingTea as Success).value.entity;
  print(updatedCupOfTea2);

  // returns OK with the updated entity in state three or four
  final TeaResult
  blackOrCheckingForMilk = await teaStateMachine.transitionCommand(
    updatedCupOfTea2,
    TeaCommands.doYouHaveMilk,
    (entity) async {
      // imagine a remote operation here which could go one of 2 ways (or fail!)
      return Random().nextBool()
          ? Success(NoMilkPlease())
          : Success(MilkPlease());
    },
  );

  print(blackOrCheckingForMilk);

  // we can display the state machine as a Mermaid diagram
  print(teaStateMachine.renderUsing(renderMermaid));
}
0
likes
140
points
69
downloads

Publisher

verified publisheronehundredandeighty.org

Weekly Downloads

Simple state management for Dart

Repository (GitHub)
View/report issues

Topics

#state-machine #testing

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

result4d

More

Packages that depend on state4d