How to create a State Management library

Sandro Maglione

Sandro Maglione

Mobile development

I started working on implementing a state machine.

It worked great. But that was not enough. How can I make it usable?

I ended up creating a state management library. Here is how 👇


Tech stack

  • dart: the state machine is implemented in plain dart (with some wizardy around generic types and type safety)
  • Flutter: the state machine was working with dart, I then wrote some code to use it with Flutter as well

Setup

Creating a new package in dart is just one command away:

dart create -t package my_new_package

I started there. Nothing more nothing less. After all this is plain dart.

Get started

Now, initially my intention was making a state machine.

I already knew well enough the theory behind state machines, and I also had my fair bit of practice with them (XState 👈).

What I didn't expected was my detour into state management, Stream, Provider, and how to connect state changes with a Flutter widget.

This was all a practical experiment, how to connect all the pieces together in a working library.

Let's dive into the implementation!

There is more.

Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.

Implementation

As the project evolved we can pinpoint 3 distinct phases 👇

Implementing a State Machine

Phase 1: State Machine.

Main challenge: how to model a type safe, usable, and flexible state machine?

The type safe part was especially important. This requires some tricky code around abstract class, generic types, and nullable/optional parameters.

abstract class State {
  void entry() {}
  void exit() {}
}
 
/// A [StateEvent] extends [State] and also has a generic [S] that extends [State] as well 🔄
abstract class StateEvent<S extends State> extends State {
  Map<Event, S> get events => {};
 
  S? next(Event event) => events[event];
}

State machines are not enough, entering Statecharts

A state machines has a final list of states and events to transition between states. Not enough in practice.

Welcome Statecharts!

A statechart expands over a state machine to add actions, context, concurrency, and much more.

This makes a state machine more useful in practice.

I added another generic parameter, with a (full) new refactoring of states and events:

abstract class Machine<Context, S extends State<Context>, E extends Event<Context>>

It works. How about streaming state in Flutter?

The machine was working great. But it was not of much use in practice.

How can I take this thing to the next level? How about an integration with Flutter?

Now, I never had some real practice using Stream in dart. This step required some serious engineering genius.

Solution: Copy-paste everything from the bloc package code, try to understand it, and make it work for my machine.

I copied everything from the bloc package with the goal of understanding how to stream statesI copied everything from the bloc package with the goal of understanding how to stream states

This is why open source is awesome! I copied all the code from bloc, and gradually removed features until I reached the core of how streaming works.

As you may have guessed, it worked!

I then did the same with flutter_bloc. At the end I created a state management library based on Provider that listens and streams states changes from a state machine.

class MachineBuilder<M extends StateStreamable<Context, S>, Context,
    S extends State<Context>> extends MachineBuilderBase<M, Context, S> {
  const MachineBuilder({
    required this.builder,
    Key? key,
    M? machine,
  }) : super(key: key, machine: machine);
 
  final MachineWidgetBuilder<Context, S> builder;
 
  @override
  Widget build(BuildContext context, Context ctx, S state) =>
      builder(context, ctx, state);
}

👉 For all the details and code snippets you can read the full article containing all the details of the implementation.

Takeaways

  • State machines (Statecharts) are awesome for modeling and managing state
  • Less theory and more practice: when you want to learn a new API dive deep into existing open source code, it will teach you way more than just watching Youtube videos
  • State management in Flutter is easy: a class has some internal state and uses a Stream to push out state updates, Flutter listens and updates the widget. Nothing more than that
  • Dart's type system is powerful in its own way: the state machine is completely type safe

At the end I have a working open source prototype. I don't intend to expand and publish this as a package on pub.dev.

Since it's open source anyone can pick it up and continue the work.

See you next 👋

Start here.

Every week I build a new open source project, with a new language or library, and teach you how I did it, what I learned, and how you can do the same. Join me and other 600+ readers.