Immutable State Management Rebuilder. How to refactor to immutability and vice versa.

MELLATI Fatah
Flutter Community
Published in
5 min readJun 3, 2020

Do you agree with Joshua Bloch in Effective Java when he stated that Classes should be immutable unless there’s a very good reason to make them mutable?

Do you like functional programming where any mutation is considered as an unwanted side effect?

Do you believe that immutable objects :

  • are simpler to construct, test, and use
  • enhance code predictability and give guarantee about their state to all object depending on it
  • are much easier to cache
  • And many more …

Do you believe in all that but still feel lazy using immutability because of its state management difficulty?

In this article I will show you a straightforward and an easy to implement immutable state management solution using states_rebuilder package.

states_rebuilder is a state management solution that differs from other good competitors by:

  • Business logic is 100% separated. Write your business logic with pure dart class without depending on any external library (Flutter and states_rebuilder included).
  • Manage the immutable and immutable state. You are free to use immutable or mutable objects or to mix them. Even you can refactor to immutability (or mutability) without affecting the user interface.
  • Very rich dependency injection system. Asynchronous dependency is injected with the same ease as synchronous dependency.
  • Side effects without the BuildContext. Navigate, get the ScaffoldState, ThemeData, or MediaQuery without BuildContext.

To focus only on the state management part, I choose to clone the provider_shopper from the official Futter sample repo with some added requirements.

provider_shopper

The added requirements are:

  • The list of products will be asynchronously loaded from a fake server. While loading the products a CircularProgressIndicator will be shown. If the server fails, a AlertDialog will be displayed with information about the error and a retry button to try another fetch.
  • Products can be removed from the list by swiping them out of the list. The removal process is done optimistically. In other words, after removing a product, it is instantly removed from the list and an asynchronous call is sent in the background to the server to remove the product. If the server fails, the deleted product is returned to the list and a SnackBar is displayed to indicate to the user the nature of the error.

The business logic part

catalog_state.dart

What’s the point here:

  • CatalogState is a pure vanilla dart class without extension or use of any external library. (no more worry about code sharing).
  • All methods return a new instance of CatalogState. Mutating an immutable object is creating a new object that reflects the mutation.
  • Methods are pure function.
  • If the result of an asynchronous method is unpredictable, we have to wait for it and meanwhile display a wait widget. In such a case, we use Future. Fetch a list of items is an example of an unpredictable asynchronous task (getItems)
  • If the result of an asynchronous method is predictable, it is wise to use the expected result and simply trust the backend service. If the backend service fails, we can return to the state just before calling the async method and display an error message. In such a case, we use Stream. Deleting an item is an example of a predictable asynchronous task (deleteItem).
  • Optionally, we can define a class that extends CatalogState to specify the initial state.(CatalogInitialState).

catalog_state.dart

Methods can be static. In this case they accept the current state as parameter.

That’s all for the business logic part of the app.

The UI part

main.dart

We use the Injector widget to inject our dependencies.

MyCatalog widget:

  • Line 4–11: We used WhenRebuilder widget to subscribe to a new ReactiveModel widget created using the future method. The future method exposes the current state of its async state. From the exposed state we invoked getItems method.
  • Line 13–35: We used onSetState parameter to invoke side effects. Here we check for the error state and display an alert dialog. From the displayed alert dialog we used the exposed ReactiveModel to call the getItems again using the refresh() method.
  • Line 36–39 : We defined the corresponding widget for OnIdle, onWaiting, onError, and onData status.
  • Line 39–65: When the list of items is ready we display it. We wrapped the list of items inside a StateBuilder widget and subscribe it to the global ReactiveModel because we want to rebuild the list items after deleting an item.

MyListItem widget:

  • Line 6–4: Get the ThemeData without the context. It is equivalent to final textTheme = Theme.of(context).textTheme.headline6 inside the build method.
  • Line 9–17: Get the ScaffoldState without the context and call showSnackBar method
  • Line 25–35: Get the global registered CatalogState ReacitveModel and call setState. setState exposed the current state. Using the current state we invoked the deleteItem method. The onError callback will be invoked if the deleteItem method throws.

AddButton widget:

  • Line 8: Get the registered instance of CartState ReactiveModel and assign it to the cartRM final variable.
  • Line 14–22: Invoke setState to mutate the state. Using the exposed current state of the CartState we call the add static method.
  • Line 23: Get the registered instance of CatalogState ReactiveModel and call the notify method to notify its listeners.

Refactor to mutability and vice versa

The interesting thing about States_rebuilder is that you can refactor to mutability or refactor to immutability without changing a single line of user interface code.

Here is the mutable version of CatalogState

With states_rebuilder, you can work with mutable objects (or immutable), and, in the future, if you want to move to immutable objects (or mutable), just make the required change to your business logic and that’s another level of separation of concerns.

Here is the sample working code.

The immutable state can be managed with the Redux approach. The flutter_bloc library is the most popular package for managing the state immutably. Here is the TodoMVC sample app rewritten following the flutter_bloc style but using states_rebuilder.

For more information on the states_rebuilder library, see the official pub.dev page

Feel free to try the states_rebuilder package and clap 👏 star 🌟 and the thumbs up 👍 it.

--

--