Using BLoC in Flutter Redux

Using BLoC in Flutter Redux

After using flutter for some time, it seems there are no the go-to way to handle ... Bloc /// /// like reducers in redux, but don't return a new state.

State management is a huge issue for the Flutter community. Coming from an Angular background I got accustomed to using Redux and therefore it made sense for me to follow the same pattern in Flutter. I also used services to encapsulate business logic and expose the state to the UI. It was a good exercise for me to try and emulate the same architecture in Flutter, especially with the emerging of the BLoC pattern.

The following article is my personal take on the matter, I don’t claim it to be the only way of achieving this architecture.

The BLoC pattern

The Business Logic Component pattern or as it is widely known the BLoC pattern is an architecture designed to decouple business logic from the view. It has helped teams to have a single codebase in regards to their business logic and expose them in various frameworks, most notable AngularDart and Flutter

In the BLoC pattern, you create a component that interfaces with the view only through Streams. This goes for all interaction either coming into the BLoC or coming out of it. The outgoing communication is straight forward as you only need to wrap your view Widget in a StreamBuilder and listen to the values coming out of the exposed outgoing stream.

class ExampleBloc {
     final StreamController<int> _outController =  StreamController<int>();

      int get out => _outController.stream;
    }


    ...

    StreamBuilder<int>(
      stream: exampleBloc.out
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return Container();
        }
        final out = snapshot.data;
        return Text('$out');
      }
    )

For the incoming communication, you have to also expose a Sink in which the view will push events from the user. In your BLoC, you subscribe to the incoming Stream and delegate the action either to a private method or a Repository, if it is about fetching data.

class ExampleBloc {
     int counter = 0;
     final StreamController<int> _incrementController =  StreamController<int>();
     final StreamController<int> _outController =  StreamController<int>();

     ExampleBloc() {
        _outController.sink.add(counter);
        _incrementController.listen(_incrementBy);
      }

     /// Do the actions or delegate in a repository
     void _incrementBy(int value) {
       counter += value;
       //Notify subscribers
       _outController.sink.add(counter);
     }

      int get out => _outController.stream;
      int get incrementBy => _incrementController.sink;
    }


    ...

    FloatingActionButton(
      onPressed: () => exampleBloc.incrementBy.add(1),
      tooltip: 'Increment By 1',
    )

What remains to be tackled is the state keeping as we really do not want to keep it in our BLoC.

Redux Store with BLoC

As we mentioned before the BLoC should not be responsible to keep the application’s state. It should delegate this responsibility to some other component dedicated to doing state management. In this example, we will use the Redux Store.

The BLoC could funnel events coming from the Sink to the Store as Actions and connect the application state from the Store to the Stream so that the Widget gets it.

/// The action to be dispatched from the BLoC to the Store
    class IncrementByAction {
      final int value;
      const IncrementByAction(this.value);
    }


    /// The state of the application as it is managed by the Store
    class AppState {
      final int counter;

      const AppState(this.counter);

      factory AppState.initial() => AppState(0);

      AppState incrementBy(int value) => AppState(counter+value);
    }


    final store = new Store<AppState>(
      TypedReducer<AppState, ChangeLocaleAction>((AppState state, IncrementByAction action) => state.incrementBy(action.value)),
      initialState: AppState.initial(),
      middleware: [],
    );


    /// The BLoC delegates all state management to the Store
    class ExampleBloc {
     final Store<AppState> store;
     final StreamController<int> _incrementController =  StreamController<int>();

     ExampleBloc(this.store) {
       _incrementController.listen(_incrementBy);
     }

     /// Dispatch the Store Action to change the AppState
     void _incrementBy(int value) {
       store.dispatch(IncrementByAction(value));
     }

      /// To connect the state from the store to the ougoing stream of the BLoC subscribe to store.onChange
      int get out => this.store.onChange.map((AppState state) => state.counter);
      int get incrementBy => _incrementController.sink;
    }
    /*
     * No changes are needed in the UI as the BLoC's api did not change.
     */
    ...
    FloatingActionButton(
      onPressed: () => exampleBloc.incrementBy.add(1),
      tooltip: 'Increment By 1',
    )
    ...
    StreamBuilder<int>(
      stream: exampleBloc.out
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return Container();
        }
        final out = snapshot.data;
        return Text('$out');
      }
    )

The obvious benefit of using this approach is that if you do not have to change the BLoC’s API and thus no change in the UI (Widgets) is needed.

However, there is another approach that kind of breaks the BLoC all Streams contract. It uses methods for actions and getters for state subscribing. It is all made possible by the use of StoreConnector.

/// The action to be dispatched from the BLoC to the Store
    class IncrementByAction {
      final int value;
      const IncrementByAction(this.value);
    }


    /// The state of the application as it is managed by the Store
    class AppState {
      final int counter;

      const AppState(this.counter);

      factory AppState.initial() => AppState(0);

      AppState incrementBy(int value) => AppState(counter+value);
    }


    final store = new Store<AppState>(
      TypedReducer<AppState, ChangeLocaleAction>((AppState state, IncrementByAction action) => state.incrementBy(action.value)),
      initialState: AppState.initial(),
      middleware: [],
    );


    /// The BLoC acts like a proxy to the store
    class ExampleBloc {
     final Store<AppState> store;

     ExampleBloc(this.store);

      /// To connect the state from the store to the ougoing stream of the BLoC subscribe to store.onChange
      int get counter => this.store.state.counter;
      void incrementBy(int value) {
          store.dispatch(IncrementByAction(value));
      }
    }
    ...
    StoreConnector<AppState, DebtorBloc>(
        converter: (store) => ExampleBloc(store),
        builder: (BuildContext context, ExampleBloc bloc) {
          return FloatingActionButton(
            onPressed: () => exampleBloc.incrementBy(1),
            tooltip: 'Increment By 1',
          );
        }
    )
    ...
    StoreConnector<AppState, DebtorBloc>(
        converter: (store) => ExampleBloc(store),
        builder: (BuildContext context, ExampleBloc bloc) {
          final out = bloc.counter;
          return Text('$out');
        }
    )

The BLoC pattern is a great way to encapsulate business logic and Redux is a great state management paradigm. Combining the two of them can create a clean logic layer in your application, which is going to help you tremendously in creating clean UI code.

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

Brave, Chrome, Firefox, Opera or Edge: Which is Better and Faster?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

ECommerce Mobile App Development | Ecommerce Mobile App Development Services

We are leading ecommerce mobile application development company. Hire our ecommerce mobile app developer for your custom Ecommerce project at competitive rates. **Know about [Top ECommerce Mobile App Development...

Mobile App development Company in Melbourne Australia

If you are seeking online **[mobile app development Melbourne Australia](http://www.efrog.com.au/ "mobile app development Melbourne Australia")**? Then Efrog Pty Ltd is the best online choice for you we are an award-winning company in Australia....

Top Mobile App Development Company in Texas

AppClues Studio is a Top Mobile App Development Company in Texas. We offer comprehensive mobile app development services for businesses that are ready to tackle innovation head-on. We specialize in building mobile apps that leverage IoT, AR/VR...