States_rebuilder

Update note (Dec-2019): states_ rebuilder know has what is called explicit reactivity and implicit reactivity. Explicit reactivity means that you have to make your logic class to extend StatesRebuilder and to explicitly notify listeners using rebuildStates method. In Implicit reactivity, your model is a pure dart class without any extension of whatsoever. It is the duty of the library to add reactivity for you, thus, leaving your model clear, simple, testable, maintainable, and framework independent.

Hi there, my name is states_rebuilder, almost all of you don’t know me. It is very difficult for newcomers to have a place in the flutter state management field with the presence of scoped_model, BloC, Redux, and many other third libraries. But anyway I’ll try.

My creator is MELLATI, the author of this article. He wrote three articles about state management in flutter to introduce me. (article 1, article 2, article 3).

How to use me?

I am the simplest state management technique to implement. Do not believe me? Just follow me.

Before going through steps of implementation, I have to point out something important. Sometimes getting new ideas is easier than baptizing it. That exactly what happened to me. I have three choices to name my logic classes:

1- BloC, but this often confused with the BloC pattern which is based on streams;

2- Model as in scoped_model, but this confuses me. Model has another meaning in MVC, MVP, and MVVM popular design patterns;

3- ViewModel, I think is the most suitable term because what The ViewModel does in MVVM is to store states, mutate states, and expose change events when the states change to update the view.

But BloC is a fancy word and I like it. So I’ll use it provided you don’t think of streams anymore.

OK, to implement me remember: I have two classes and you have two steps:

I have two classes: StatesRebuilder and StateBuilder, you follow two steps:

1- Your logic class, YourBloc, must extend my StatesRebuilder class to be able to use my rebuildStates method:

This is a typical implementation of my StatesRebuilder:

Import "states_rebuilder/states_rebuilder.dart"; 1- Explict reactivity: class YourBloc { var yourVar; /// You have two alternatives:

/// First alternative : Notify all listeners yourMethod1() { // some logic staff;

yourVar = yourNewValue; rebuildStates();

} // Second alternative (tag alternative): filter notification by tag yourMethod2() { // some logic staff;

yourVar = yourNewValue; rebuildStates([“yourStateTag1”, “yourStateTag2”]);

}

} // Update:Dec-2019

2- Implicit reactivity:

A pure vanilla dart class without extantion or notification. class YourBloc { var yourVar; yourMethod1() { // some logic staff;

yourVar = yourNewValue;

}

}

rebuildStates method is equivalent to notifyListeners in the scoped_model.

As you can see, you have two alternatives to call the rebuildStates method:

The first alternative (notify all listeners): When rebuildStates is called without arguments it will notify all listener widgets.

The second alternative (or the tag alternative): in the UI you define tags to the widgets that you want to be rebuilt from the BloCs. In yourMethod you call the rebuildStates with any number of tags you want. (rebuildStates([“yourStateTag1”, “yourStatetTag2”]); )

The question is: How to add tags to widgets? The answer is in the next step.

2- In your UI and after providing your blocs, wrap any widget you want to be able to rebuild from the BloC with Statebuilder wiget, give it a tag and define the list of models you want to use the tag from. This is the first alternative:

StateBuilder(

models : [yourBloc]

tag: “yourStateTag1”, // tag is optional

builder: (_,__) => YourChildWidget(yourBloc.yourVar),

)

With this, you can call yourMethod1 from any place in the UI to rebuild the YourChildWidget.

For example, in another place in your UI even from another file, you can have:

RaisedButton(

..

onPressed : yourBloc.yourMethod1,

)

tag parameter is optional and can be omitted. In this case, the widget will be notified using rebuildStates() without arguments.

states_rebuilder creates a default tag to each StateBuilder widget which is the BuildContext of the widget.

StateBuilder(

models : [yourBloc]

builder: (BuildContext context, _) => YourChildWidget(

//context is the default tag

yourBloc.yourVar,

onPressed: ()=>yourBloc.yourMethod2(context),

),

)

The default tag is very useful if you are to render a list of widgets using ListView or any similar widget. Imagine you have 100 ListTiles, it’s not practical to give them all tags. This gives you the ability to rebuild any of these 100 ListTiles from the blocs without rebuilding the whole ListView (see the challenge section).

StateBuilder offers more options to use. This is the class constructor:

StateBuilder<T>(

onSetState: (BuildContext context, ReactiveModel<T> model){

/*

Side effects to be executed after sending notification and before rebuilding the observers. Side effects are navigating, opening the drawer, showing snackBar,...

*/

}, onRebuildState: (BuildContext context, ReactiveModel<T> model){

// The same as in onSetState but called after the end rebuild process.

}, initState: (BuildContext context, ReactiveModel<T> model){

// Function to execute in initState of the state.

}, dispose: (BuildContext context, ReactiveModel<T> model){

// Function to execute in dispose of the state.

}, didChangeDependencies: (BuildContext context, ReactiveModel<T> model){

// Function to be executed when a dependency of state changes.

}, didUpdateWidget: (BuildContext context, ReactiveModel<T> model, StateBuilder oldWidget){

// Called whenever the widget configuration changes.

}, afterInitialBuild: (BuildContext context, ReactiveModel<T> model){

// Called after the widget is first inserted in the widget tree.

}, afterRebuild: (BuildContext context, ReactiveModel<T> model){

/*

Called after each rebuild of the widget. The difference between onRebuildState and afterRebuild is that the latter is called each time the widget rebuilds, regardless of the origin of the rebuild. Whereas onRebuildState is called only after rebuilds after notifications from the models to which the widget is subscribed.

*/

}, // If true all model will be disposed when the widget is removed from the widget tree

disposeModels: true, // A list of observable objects to which this widget will subscribe.

models: [model1, model2] // Tag to be used to filer notification from observable classes.

// It can be any type of data, but when it is a List,

// this widget will be saved with many tags that are the items in the list. tag: dynamic builder: (BuildContext context, ReactiveModel<T> model){

/// [BuildContext] can be used as the default tag of this widget.

/// The model is the first instance (model1) in the list of the [models] parameter.

/// If the parameter [models] is not provided then the model will be a new reactive instance.

}, builderWithChild: (BuildContext context, ReactiveModel<T> model, Widget child){

///Same as [builder], but can take a child widget containing the part of the widget tree that we do not want to rebuild.

/// If both [builder] and [builderWithChild] are defined, it will throw.

},

//The child widget that is used in [builderWithChild]. child: MyWidget(),

)

initState, dispose, didChangeDependencies, didUpdateWidget have the same meaning as in StatefulWidget. For example, you can execute some code in the initState to fetch some data from an API. You can pass the tag (context by default) to the bloC to rebuild it when you get the data.

StateBuilder(

initState : (context,_) => yourBloc.yourInitMethod(context),

builder: (_) => YourChildWidget(

yourBloc.yourVar,

),

)

in the BloC you have something like this:

class YourBloc extends StatesRebuilder{ var yourVar; yourInitMethod(context )async { await fetchItemsFromAPI();

yourVar = yourNewValue; rebuildStates([context]);

}

Endnote: If you followed my last articles you may have noticed that I changed the naming convention a little bit:

BloCSetting becomes StatesRebuilder

rebuildWidgets becomes rebuildStates.

How do I manage the state?

As you can see, both scoped_model and BloC techniques end in calling setState() method somewhere in the code. scoped_model calls setState() inside an animatedWidget, and BloC inside StreamBuilder. I’m not an exception, I use the observer pattern and call setState(), in the update() method of the StateBuilder widget:

and when you call rebuildStates method, you and calling the update() method of all registered StateBuilder widgets.

To the challenge:

the MainBloc is as simple as in scoped_model MainModel. actually it looks similar to the scoped_model logic with one and important difference:

I add a variable of type State named tappedCardTag. this variable is mutated in showDetailed method to hold the tag of the tapped card. When increment method is called the item of index equals detailedIndex is incremented and ONLY DETAILED CONTAINER (detailedWidgetState) AND THE TAPPED CARD (tappedCardTag) ARE REBUILT.

NB: I provided the BloC globally for simplicity.

the UI part of the challenge:

lines 09–36 : StateBuilder is defined to make the horizontal ListView reactive.

— — — line 12: fetchItems(context) is called from the initState parameter, “context” here holds the tag of the widget to be used inside the mainBloc.

— — — line 13: check if items List is null, then show a circular progress indicator, if not null show the ListView;

lines 25–37 : StateBuilder is defined to make each individual itemCard reactive. It is defined using default tag because we will rebuild the widget from a function (showDetailed) called from the inside:

— — — line 31: the builder parameter is defined with “context” as function argument; the “context” is passed to showDetailed(randColor, index, context) to update the tappedCardTag to hold this tag of the tapped card in the bloc.

lines 42–54 : StateBuilder is defined to make each individual detailed container reactive. It is defined using user-defined tag because we will rebuild the widget from a function (showDetailed) called from the outside.

That is all, the simplest of the tree approach. Let’s see if it passes the challenge:

Voila, the states_rebuilder wins the challenge.

little bug:

in the GIF below, if you tapped on the detailed container to increment the first itemCard, then if you scroll the ListView to the right until the first itemCard no longer visible. now if you scroll back to the first itemCard and top on the detailed container the itemCard does not update.

This behavior is expected because ListView.builder deposes itemCards that are not displayed and creates them if they are displayed again. In our case when we scroll back to display the first itemCard, a new BuildContext is created which is not the same as the BuildContext store in the tappedCardTag.

To fix this bug we have to update the value of tappedCardTag to hold the newly create BuildContext of the itemCard. Here how to do it:

line 21–32 in UI code above are updated by adding the highlighted line:

in the initState parameter, we call a new method from the mainBloc updateTappedState with state and index as parameter. in the bloc we add the updateTappedState method:

Each time an ItemCard is created, the updateTappedTag is called. we check if the index of the itemCard is equal to the detailedIndex and assign the passed state to the tappedCardTag. that all to do to fix the bug

Animation with states_rebuilder

states_rebuilder is so simple and powerful that we can separate animation setting from the UI. With states_rebuilder and only with it, animation setting is done inside the BloC and the UI is totally free of any animation mess.

look at this UI part:

the other part of the code is the same as the basic example. As you can see there nothing related to animation in the UI except FractionalTranslation which is a simple widget from the basic flutter framework.

This is how increment() methods look like to animate the cards:

here is the resulted animation from the above code:

Actually when it comes to animation I have many challenges. With states_rebuilder I can control the animation of hundreds of tiny widgets. look at this LED sign:

this is the UI part:

a simple GridView.count and a Container with an animated color value form the mainBloc.

super easy

In conclusion:

For small app, the states_rebuilder is the simplest with no side effects:

Schematic representation of one task app

When it comes to more complex app, the state management is still simple with full control on which widget to rebuild. The complexity lays in how much your logic is complex and on the number of pages you have but not on the state management.

Schematic representation of multi task app

states_rebuilder is the easiest, the most clean and the most powerful. With states_rebuilder you can achieve better-designed app and cleaned logic with little code.

Now to the prize distribution ceremony :

No matter which of the state management technique you use and support, let’s show our great fluttersmanship (sportsmanship) and applaud the winner with +50 claps 👏 👏.

The link to the full code of this article will be available in comments sooner.