{"id":28403371,"url":"https://github.com/workiva/state_machine","last_synced_at":"2026-03-03T07:03:24.758Z","repository":{"id":29592807,"uuid":"33132764","full_name":"Workiva/state_machine","owner":"Workiva","description":"Easily create a finite state machine and define legal state transitions. Listen to state entrances, departures, and transitions.","archived":false,"fork":false,"pushed_at":"2026-03-03T05:09:32.000Z","size":174,"stargazers_count":65,"open_issues_count":9,"forks_count":24,"subscribers_count":38,"default_branch":"master","last_synced_at":"2026-03-03T06:44:31.086Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Workiva.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2015-03-30T15:53:42.000Z","updated_at":"2026-03-03T05:09:22.000Z","dependencies_parsed_at":"2024-12-06T22:29:37.856Z","dependency_job_id":"9dc3d1d4-8072-4fa1-a9e1-c84e12d53d50","html_url":"https://github.com/Workiva/state_machine","commit_stats":{"total_commits":127,"total_committers":23,"mean_commits":5.521739130434782,"dds":0.8031496062992126,"last_synced_commit":"c6808099905e8e2768879794b5f577894bb58728"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/Workiva/state_machine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fstate_machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fstate_machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fstate_machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fstate_machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Workiva","download_url":"https://codeload.github.com/Workiva/state_machine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workiva%2Fstate_machine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30035486,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T06:58:30.252Z","status":"ssl_error","status_checked_at":"2026-03-03T06:58:15.329Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-01T17:36:35.038Z","updated_at":"2026-03-03T07:03:24.724Z","avatar_url":"https://github.com/Workiva.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dart State Machine\n[![Pub](https://img.shields.io/pub/v/state_machine.svg)](https://pub.dartlang.org/packages/state_machine)\n[![Build Status](https://travis-ci.org/Workiva/state_machine.svg?branch=master)](https://travis-ci.org/Workiva/state_machine)\n[![codecov.io](https://codecov.io/gh/Workiva/state_machine/branch/master/graph/badge.svg)](http://codecov.io/github/Workiva/state_machine?branch=master)\n[![documentation](https://img.shields.io/badge/Documentation-state_machine-blue.svg)](https://www.dartdocs.org/documentation/state_machine/latest/)\n\n\u003e Easily create a finite state machine and define legal state transitions. Listen to state entrances, departures, and transitions.\n\n## Getting Started\nImport the `state_machine` package:\n\n```dart\nimport 'package:state_machine/state_machine.dart';\n```\n\n### Create a Machine\nOnce created, the `StateMachine` will be used to create states and state transitions.\n\n```dart\nStateMachine light = new StateMachine('light');\n```\n\n### Define a Set of States\nUse the machine to create all required states. A string name is required for ease of debugging.\n \n```dart\nState isOn = light.newState('on');\nState isOff = light.newState('off');\n```\n\nIt's recommended that states be named in the format \"is[State]\".\nThis may seem strange at first, but it has two main benefits:\n\n1. It helps differentiate states from transitions, which can be confusing\nsince many words in English are the same as a verb and an adjective\n(\"open\" or \"secure\", for example).\n2. It reads better when calling the state to determine if it's active,\nas will be demonstrated later.\n\n### Define the Legal State Transitions\nBy defining legal state transitions, you can prevent certain actions based on the current state of the machine.\nDefining a state transition requires a name (again for ease of debugging), a list of valid \"from\" states, and\nthe state to transition the machine to.\n\n```dart\nStateTransition turnOn = light.newStateTransition('turnOn', [isOff], isOn);\nStateTransition turnOff = light.newStateTransition('turnOff', [isOn], isOff);\n```\n\n### Start the Machine\nBefore executing any state transitions, the machine should be started at a specific starting state.\n\n```dart\nlight.start(isOff);\n```\n\n### Executing a State Transition\nThe `StateTransition` class implements `Function` so that you can simply call a transition to execute it.\n\n```dart\nlight.start(isOff);\nturnOn(); // transitions machine from \"isOff\" to \"isOn\"\n```\n\n### Determining the Active State\nThe `StateMachine` instance exposes a `current` state property which allows you to retrieve the machine's current state\nat any time.\n\n```dart\nlight.start(isOff);\nlight.current == isOff; // true\n```\n\nAdditionally, the `State` class implements `Function` so that you can simply call a state to determine if it's active.\n\n```dart\nlight.start(isOff);\nisOff(); // true\nisOn();  // false\n```\n\n### Listening to State Transitions\nThe `StateTransition` class exposes a `listen()` method that allows you to listen to the transition and receive an\nevent every time the transition executes.\n\n```dart\nturnOn.listen((StateChange change) {\n  print('Light transitioned from ${change.from.name} to ${change.to.name}');\n});\nlight.start(isOff);\nturnOn(); // \"Light transitioned from off to on\"\n```\n\n### Passing Data with a State Transition\nState transitions accept an optional payload in case you need to pass data along to listeners.\n\n```dart\nturnOn.listen((StateChange change) {\n  print('Light turned on. Wattage: ${change.payload}');\n});\nlight.start(isOff);\nturnOn('15w'); // \"Light turned on. Wattage: 15w\"\n```\n\n### Listening for State Entrances and Departures\nThe `State` class exposes two streams so that you can listen for the state being entered and the state being left.\n\n```dart\nisOff.onLeave.listen((StateChange change) {\n  print('Left: off');\n});\nisOn.onEnter.listen((StateChange change) {\n  print('Entered: on');\n});\nlight.start(isOff);\nturnOn(); // \"Left: off\"\n          // \"Entered: on\"\n```\n\n### Wildcard State and State Transitions\nThe `State` class exposes a static instance `State.any` that can be used as a wildcard when defining a state transition.\n\n```dart\nStateMachine machine = new StateMachine('machine');\nState isFailed = machine.newState('failed');\n\n// This transition will be valid regardless of which state the machine is in.\nStateTransition fail = machine.newStateTransition('fail', [State.any], isFailed);\n```\n\n### Illegal State Transitions\nWhen you create state transitions, you must define the list of valid \"from\" states. The machine must be in one of these\nstates in order to execute the transition. If that's not the case, an `IllegalStateTransition` exception will be thrown.\n\n```dart\n// Consider a door with the following states and transitions.\nStateMachine door = new StateMachine('door');\n\nState isOpen = door.newState('open');\nState isClosed = door.newState('closed');\nState isLocked = door.newState('locked');\n\nStateTransition open = door.newStateTransition('open', [isClosed], isOpen);\nStateTransition close = door.newStateTransition('close', [isOpen], isClosed);\nStateTransition lock = door.newStateTransition('lock', [isClosed], isLocked);\nStateTransition unlock = door.newStateTransition('unlock', [isLocked], isClosed);\n\n// Let's transition the door from open, to closed, to locked.\ndoor.start(isOpen);\nclose();\nlock();\n\n// In order to open the door, we must first unlock it.\n// If we try to open it first, an exception will be thrown.\nopen(); // throws IllegalStateTransition\n```\n\n### Canceling State Transitions\nState machines have a set of legal state transitions that are set in stone and provide the required structure.\nBut, there may be scenarios where a state transition may or may not be desirable based on additional logic.\nTo handle this, state transitions support cancellation conditions.\n\n```dart\n// Consider two state machines - a person and a door.\n// The door can be locked or unlocked and the person\n// can be with or without a key.\nStateMachine door = new StateMachine('door');\nState isLocked = door.newState('locked');\nState isUnlocked = door.newState('unlocked');\nStateTransition unlock = door.newStateTransition('unlock', [isLocked], isUnlocked);\n\nStateMachine person = new StateMachine('person');\nState isWithKey = person.newState('withKey');\nState isWithoutKey = person.newState('withoutKey');\nStateTransition obtainKey = person.newStateTransition('obtainKey', [isWithoutKey], isWithKey);\n\ndoor.start(isLocked);\nperson.start(isWithoutKey);\n\n// Add a cancellation condition for unlocking the door:\n// If the person is without a key, cancel the unlock transition.\nunlock.cancelIf((StateChange change) =\u003e isWithoutKey());\n\nunlock(); // false (canceled)\nisUnlocked(); // false\nobtainKey();\nunlock(); // true (not canceled)\nisUnlocked(); // true\n```\n\n\n## Example\n\nTo run the example:\n```\ndart pub global activate webdev\nwebdev serve example:8080\nopen http://localhost:8080/\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworkiva%2Fstate_machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fworkiva%2Fstate_machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworkiva%2Fstate_machine/lists"}