Adding Type-Safety to Upstate

Jonathan Aird
3 min readApr 29, 2020

Flutter state management should be simple (but also type-safe)!

I recently introduced Upstate, a simplified state management library for Flutter that is meant to be very easy to learn and yet powerful and extensible. You can check out my introductory article here. If you’re a keen developer, you will realize that as is, using Upstate the way I showed isn’t type-safe! Check out this article for a quick explanation of how that can cause problems.

To make the library work properly, the return type of [] operators and the call method are dynamic. That means you might accidentally call a function on the wrong type of object without throwing any errors.

I included a neat feature which lets you specify what kind of object you’re expecting to the call method:

var state = StateObject({'counter':0}); 
var path = StatePath(['counter']);
var count = state<StateValue>(path);

If you specify the wrong type, you’ll get an error at runtime:

count = state<String>(path); //throws an error.

This is nifty because doing this will also give you code completion suggestions and show errors in our IDE before running your build. You could also specify a type in your variable declaration:

StateValue count = state(path);
String wrongType = state(path); //throws an error

However, there was no way of enforcing this behavior and you could still optionally get the counter state element in a non-type-safe manner. Additionally, there was no way of specifying to a state builder what types of state elements you were expecting since getting the state elements happens behind the scenes.

Introducing the typeSafety option

With Upstate 0.5.2, there is now a typeSafety option for state objects:

var state = StateObject({'counter':0}, 
typeSafety:TypeSafety.complete);

How does it work? I had to get a little creative with Dart typing features but the solution is quite simple. State paths now come with a generic that specifies the exact type you expect to find at that path. If you have this option enabled but don’t specify an expected type or specify the wrong type, you will run into an error:

var unsafePath = StatePath(['counter']);
var safePath = StatePath<StateValue>(['counter']);
//The expected type is stored in the expectedType property
print(safePath.expectedType)//prints StateValue<dynamic>
var count = state(safePath); //runs without error
count = state(unsafePath); //throws an error

When this option is enabled, using the [] operators will throw an error as there’s no way to make them type-safe (if you have a deeply nested map/list tree, you can use TypeSafety.basic to enable the [] operators for state maps or state lists that are not the root StateObject).

And that’s all we had to change! Cheers and happy coding!

Please note! You MUST use good naming conventions when storing your state paths in variables. It is possible to accidentally provide the wrong state path to your state object and call a function you didn’t mean to. Consider using the expected type in the variable names of your paths.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response