BLoC pattern with a simple example

And again about BLoC using the classic Flutter counter example.







Reading some articles about reactive programming and using the BLoC pattern in applications, I realized that I was not catching up with something. As usual, there is not enough time for everything, but now, there was a free hour and there is strength - itโ€™s decided, Iโ€™ll write the simplest Flutter application with the BLoC pattern.







Under cat animashka applications and explanations why I wrote it that way. Very interesting is the opinion of the community.







image







Yes, we have already written about this pattern many times, but anyway, there are no clear instructions and application rules for it, and the question often arises of how to correctly implement the logic in the application.







The purpose of the article is to bring a little clarity to myself and, I hope, to readers.







So, the definition of the pattern, as it was voiced by Google engineers - BLOC is a simple class in which:







  1. all outputs streams
  2. all inputs are streams
  3. this class should remove logic from the visual interface


To implement this pattern, we, if necessary, can use the rxdart library, but we may not use it.







This will not remove reactivity as you might think. As the creators of the package themselves write, rxdart adds functionality to the already built-in rich features of the Dart language in working with streams.







application





So, we take the application with counters, which is created automatically when the project is created and try to rewrite it using the BLoC pattern.







Tasks:







  1. Remove all logic from widgets
  2. In the BLoC class, receive only input streams and output only output streams. For example, the function proposed in the comments to the article .


image







it doesnโ€™t suit us, since it violates the rule of passing only flows to the class.







Decision:







  1. We remove all the logic from the widgets. We make the Stateless class and stop updating states using setState.
  2. We display the meter readings in different places using the built-in widget, which is made specifically for displaying data from streams - StreamBuilder.


class MyHomePage extends StatelessWidget { CounterBloc counterBloc = CounterBloc(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: StreamBuilder<int>( stream: counterBloc.pressedCount, builder: (context, snapshot) { return Text( 'Flutter Counter Bloc Example - ${snapshot.data.toString()}', ); }), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), StreamBuilder<int>( stream: counterBloc.pressedCount, builder: (context, snapshot) { return Text( '${snapshot.data.toString()}', style: Theme.of(context).textTheme.display1, ); }), ], ), ), floatingActionButton: Container( width: 100.0, height: 100.0, child: FloatingActionButton( onPressed: () { counterBloc.incrementCounter.add(null); }, tooltip: 'Increment', child: Text( "+ \n send \n to BLoC", textAlign: TextAlign.center, ), ), ), ); } }
      
      





  1. We create a separate class where we implement the BLoC pattern:

    3.1 All class properties and methods are hidden.

    3.2 To receive and transmit the state, we use streams that are visible from the outside using getters (but [a cool article about them]) ( https://habr.com/en/post/464095/ ).


 class CounterBloc { int _counter; CounterBloc() { _counter = 1; _actionController.stream.listen(_increaseStream); } final _counterStream = BehaviorSubject<int>.seeded(1); Stream get pressedCount => _counterStream.stream; Sink get _addValue => _counterStream.sink; StreamController _actionController = StreamController(); StreamSink get incrementCounter => _actionController.sink; void _increaseStream(data) { _counter += 1; _addValue.add(_counter); } void dispose() { _counterStream.close(); _actionController.close(); } }
      
      





That's all, we have got a working example, in which we transmit and receive state in streams and display data in the necessary widgets without applying any logic in the visual part.







Thus, we can separate all calculations, data transfers, etc. from the interface. For example, if you add different labels to the counter depending on the quantity and take them from an external database, then all this logic will already pass through BLoC - our interface will not know anything about it.







Note 1: note that we are creating an instance of the class> CounterBloc counterBloc = CounterBloc (); and then we get data from it. If we need this data on different screens (in spaced classes), then we can either use Inherited widgets for transfer or make Singleton from our class.







Github example code







Good coding to everyone!







Addition 1







Based on suggestions from the community, Iโ€™ll try to choose a time and write a sequel in which







  1. Let's see how to free memory when using Stateless and Stateful widgets.
  2. Let's make the second screen and pass the state there using

    2.1 Inherited Widgets

    2.2 Provider

    2.3 Singleton
  3. We will write tests and see how they work with 2.1, 2.2, 2.3


If you have ideas what else to check, see - write in comments.








All Articles