In hisexcellent article: https://medium.com/flutter-community/flutter-oneyearchallenge-scoped-model-vs-bloc-pattern-vs-states-rebuilder-23ba11813a4f, Meftah explains several frameworks that can be used to solve the problem how to maintain state in a flutter app, in such a way that all classes that contain business logic is completely separated from the view classes. Meftah compares two other frameworks: ‘Scoped-model’ and ‘BloC-pattern’ and after that offers his own implementation ‘State-Rebuilder’. He not only describes these frameworks, it is worked out in three different fully functional working flutter apps! Well this year just started, so why not make another challenge, on this same topic, and that is exactly what this article is about.

Yet another framework to solve this problem!? No not really, I will also present a fully functional working app similar to the 3 apps from above, but this one is not a new framework, but instead is based on the general purpose Pub lib: flutter-eventbus.

To recap, for all state-mgmt alternatives, Meftah created an app, that look like this:

thanks to Meftah Melatti

In this publication I describe a similar app that I created. At the same time I have made this app much more challenging, by introducing a second BloC. Hence the title #TwoYearChallenge.
BloC-1 increments the card score with 1. Bloc-2 increments the card score with 2. That’s not a big issue, the extra complexity is that the user can switch between these two BloC’s.
In addition I have added one extra row, that shows the total score.

The resulting app looks like this:

I don’t see how this problem can be solved in the above frameworks, without introducing if-then-else inside the main view page. In my app (that I will present below) however this can be achieved.

The full source can be found here:
https://github.com/robinbakkerus/flutter_state_mgmgt
hence I will only briefly describe each class.

/model/card_data.dart
This is a very simple Dart class that contains the state data.

/events/card_events.dart
Here a single instance of the EventBus is initiated:

class CardEvents {
static final EventBus _sEventBus = new EventBus();

It also contains all the events that can be fired plus the corresponding event values. These are regular Dart classes for example:

class ActivateBlocEvent {
ActivateBlocEvent(this.blocName);
final String blocName;
}

Finally It contains a number of convenience shortcuts to make easier for client to fire events and or listen on event. For example:

static void fireCardTabbed(CardData card) =>
_sEventBus.fire(new CardTabbedEvent(card));

and

typedef void OnCardTabbedFunc(CardTabbedEvent event);
static void onCardTabbed(OnCardTabbedFunc func) =>
_sEventBus.on<CardTabbedEvent>().listen((event) => func(event));

/bloc/cards_bloc.dart

In this file an interface (abstract class) CardBloc is defined, and two implementations of this interface. Note that these implementations don’t extend anything special to make state-management possible. In only contains an import to the card_event file from above. There is a factory class that instantiated the two implementations.

Hence all implementations of a CardBloC are stored in a List, and this List is used later on, in the main gui.
The last command in the internal constructor, is the first time in this article, that we see the EventBus in action. Not directly but via a shortcut we defined above. It says, that if an ‘ ActiveBlocEvent’ is fired,I want to hear about, and the _onActiveBloc() method should be triggered:

void _onActivateBloc(ActivateBlocEvent event) {
cardBlocs.forEach((cb) {
(cb.blocName == event.blocName) ? cb.activate() : cb.deActivate();
});}

The ActivateBlocEvent has on property, the blocName (see the snippet above), and using this blocName, the corresponding bloc is activated, all the others are deactivated.

The business logic of the CardBloc implementation itself is trivial, the interesting parts are the following.

In the base constructor we setup listeners for incoming events, and an event is fired that contains all the (top) cards, the detailCard (which is null at this point), and the initial total score (also 0).

The most interesting part is this base method:

Remember we don’t have one BloC, we have two BloC’s (and I can easily add more). Only one BloC is active, hence the first line:

if (!this.active) return;

Based on what card is tabbed, a top card, or the detail, different actions are performed. When the detail is clicked the score is incremented, see the snippet below. Otherwise we lookup the actual card based on the card number from the event, and we make one the new detailCard.

The _incScore() method is the only abstract method in the base class and hence implemented in the 2 impls. For example:

So we look for the (top) card, and increments its score, and we copy this value to the detail card.
Finally two events are fired, one for main view page, and for the card widget. Which is the next topic

card/card_widget.dart

This contains a stateful flutter widget, to display the CardData properties (the card score) in the main gui.
Note that file, does not have an import to card_bloc file, a likewise the card_bloc no imports to a gui file. The (for this topic) interesting part is:

Note that in card_bloc file discussed before, this event was fired:

CardEvents.fireCardSetState(CardBloc._detailCard.cardData);

And because in the constructor a listener was setup for each card, all card’s will hear about this event and trigger this _handleEvent() method. So we cannot simply copy the value from the event, but first we check if it concerns this card. (this literally).

Note: An alternative (maybe better?) approach is to make sure that only the detail-card, and corresponding top-card are listening to this event. This can be achieved by calling onClose() on the eventBus, and enable it again in the active() method.

Finally we have main view page:
main.dart

In the constructor we setup a listener, a fire an event:

_ListDisplay() {
CardEvents.onCardsUpdated((e) => _onCardsUpdated(e));
CardEvents.fireCardSetup();
}

The fireCardSetup() triggers the active CardBloc implementation, that in its turn fires an event for which we just setup a listener, a thus triggers:

And that completes the entire puzzle.

Conclusion

The big difference between this setup and ‘Scoped-model’, ‘BloC-pattern or ‘ State-rebuilder’ is that nowhere a special framework class must be extend-ed. So all the BloC, widget and assemble view classes are plain flutter files. The only extra dependency is to a general purpose EventBus, and a file that contains all events its corresponding event-data classes.

I hope you liked this article and gives new ideas how to tackle state-management problems.

Happy coding…
Robin

--

--