• Have you already heard about reactive programming? RxDart is a reactive functional programming library for Dart language, based on ReactiveX. Dart already has a decent package to work with Streams, but RxDart comes to adds functionality on top of it. But now you might be asking, what’s Stream?

    Streams and Sinks

    Streams represent flux of data and events, and what it’s important for? With Streams, you can listen to data and event changes, and just as well, deal with what’s coming from the Stream with listeners. How it can be applied to Flutter? For example, we have a Widget in Flutter called StreamBuilder that builds itself based on the latest snapshot of interaction with a Stream, and when there’s a new flux of data the Widget reload to deal with the new data. Widget Weekly of Flutter Dev Channel offers great content about how the StreamBuilder works. And about Sinks? If we have an output of a data flux, we also need an input, that’s what Sinks is used for, seems simple right? Now let’s see about the BLoC pattern and how can we combine both concepts into a great Flutter app.

    BLoC Pattern

    The BLoC(Bussiness Logic Component) Pattern was announced officially by Paolo Soares in the Dart Conference 2018. If you saw the announcement video, probably you realized that the initial proposal was to reuse the code related to the business logic in other platforms, in this case, Angular Dart. Shortly what the pattern seeks for, is take all business logic code off the UI, and using it only in the BLoC classes. It brings to the project and code, independence of environment and platform, besides put the responsibilities in the correct component. And now our talk will make much more sense, because BLoC Pattern only relies on the use of Streams. For more deatail check my article 

    A look at RxDart

    RxDart is now (at the moment of this post) in the version 0.21.0. And here I’m going to talk about some objects that the library brings to us.

    Observable<T> class

    Observable allow us to send a notification to Widgets which is observing it and then deal with the flux of data. Observable class in RxDart extends from Stream, which implies in some great things:

    • All methods defined on the Stream class exist on Observable as well.
    • All Observable can be passed to any API that expects a Dart Stream as an input (including for example StreamBuilder Widget).

    PublishSubject<T> class

    This one is pretty simple. This Subject allows sending data, error and done events to the listener. Here it will work with Sinks, which we were talking about before. See the example above:

    PublishSubject<int> subject = new PublishSubject<int>();
    
    /*this listener below will print every integer added to the subject: 1, 2, 3, ...*/subject.stream.listen(print);
    subject.add(1);
    subject.add(2);
    
    /*but this listener below will print only the integer added after his initialization: 3, .../*subject.stream.listen(print);
    subject.add(3);

     

    BehaviorSubject<T> class

    This one is similar to the PublishSubject. It also allows sending data, error and done events to the listener, but the latest item that has been added to the subject will be sent to any new listeners of the subject. But don’t you worry, after that, any new events will be appropriately sent to the listeners. See the example above:

    BehaviorSubject<int> subject = new BehaviorSubject<int>();subject.stream.listen(print); // prints 1,2,3 subject.add(1);
    subject.add(2);
    subject.add(3);
    
    subject.stream.listen(print); // prints 3

     

    ReplaySubject<T> class

    The ReplaySubject allow us the same: sending data, error and done events to the listener. But with a crucial difference here. As items are added to the subject, the ReplaySubject will store them and when the stream is listened to, those recorded items will be emitted to the listener. See the example above:

    ReplaySubject<int> subject = new ReplaySubject<int>();
    
    subject.add(1);
    subject.add(2);
    subject.add(3);
    
    subject.stream.listen(print); // prints 1, 2, 3
    

     

    Now let’s see it in practice

    In this article I will show to you a simple example of using RxDart and principles of BLoC pattern. Let’s start it.

    A great way to start it, is from the beginning: Flutter Hello World. Probably you are familiarized with the increment function on the app, but to make more let’s create the decrement function as well. So first of all, create a flutter project and import rxdart to your project. Let’s code:

    import 'package:flutter/material.dart';

    void main() => runApp(new MyApp());

    class MyApp extends StatelessWidget {

      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: new MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }

    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);

      final String title;

      @override
      _MyHomePageState createState() => new _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;

      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }

      void _decrementCounter() {
        setState(() {
          _counter--;
        });
      }

      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
          ),
          body: new Center(
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new Text('You have pushed the button this many times:',),
                new Text('$_counter', style: Theme.of(context).textTheme.display1),
              ],
            ),
          ),
          floatingActionButton: new Column(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
            new Padding(padding: EdgeInsets.only(bottom: 10), child:
              new FloatingActionButton(
                onPressed: _incrementCounter,
                tooltip: 'Increment',
                child: new Icon(Icons.add),
              )
            ),
            new FloatingActionButton(
              onPressed: _decrementCounter,
              tooltip: 'Decrement',
              child: new Icon(Icons.remove),
            ),
          ])
        );
      }
    }

    As you can see, this code implements the increment and decrement function, but still doesn’t apply the BLoC pattern or even Streams. This code works and it’s pretty simple, but if you took attention you’ll see that we have two logic business function in the UI code: increment and decrement. So imagine if this app was a big app that you was working hard, but now the requirement has been changed and the increment needs to add two at time. Do you agree with me (that in this case) a requirement changing in the business logic shouldn’t affect UI code, right? If yes, great! You got it, that is the point to separate responsibilities.

    Now let’s separate it and use what we have learned so far. Let’s create our CounterBloc class:

    import 'package:rxdart/rxdart.dart';

    class CounterBloc {

      int initialCount = 0; //if the data is not passed by paramether it initializes with 0
      BehaviorSubject<int> _subjectCounter;

      CounterBloc({this.initialCount}){
       _subjectCounter = new BehaviorSubject<int>.seeded(this.initialCount); //initializes the subject with element already
      }

      Observable<int> get counterObservable => _subjectCounter.stream; 

      void increment(){
        initialCount++;
        _subjectCounter.sink.add(initialCount);
      }

      void decrement(){
        initialCount--;
        _subjectCounter.sink.add(initialCount);
      }

      void dispose(){
        _subjectCounter.close();
      }
      
    }

    Great! Now let me explain the code above. We created a class called CounterBloc which imports the rxdart library. In this case, we need to receive the initialCount, that allow us to know from which number our counter should begin. I choose for this example the BehaviorSubeject, and then I initialized the Subject with the data passed by parameter, in other words, when the Widget become a listener of the Subject the first value passed through the stream will be the initialCount which was set in the CounterBloc constructor. Now let’s talk about the methods. In this case, we have four methods in the class:
    • increment(): increment the initialCount and send to the Subject listeners by Sink the new value.
    • decrement(): decrement the initialCount and send to the Subject listeners by Sink the new value.
    • dispose(): close the opened subject.
    • counterObeservable(): return an Observable of the Subject, in other words, the object which will be used to notify the Widgets when changes happen in the Stream.

    Now that we have the BLoC class created let’s see integrating it with the UI.

    import 'package:flutter/material.dart';
    import 'package:bloc_example/bloc/CounterBloc.dart';

    void main() => runApp(new MyApp());

    class MyApp extends StatelessWidget {

      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: new MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }

    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);

      final String title;

      @override
      _MyHomePageState createState() => new _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {

      CounterBloc _counterBloc = new CounterBloc(initialCount: 0);

      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
          ),
          body: new Center(
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new Text('You have pushed the button this many times:'),
                new StreamBuilder(stream: _counterBloc.counterObservable, builder: (context, AsyncSnapshot<int> snapshot){
                  return new Text('${snapshot.data}', style: Theme.of(context).textTheme.display1);
                })
              ],
            ),
          ),
          floatingActionButton: new Column(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
            new Padding(padding: EdgeInsets.only(bottom: 10), child:
              new FloatingActionButton(
                onPressed: _counterBloc.increment,
                tooltip: 'Increment',
                child: new Icon(Icons.add),
              )
            ),
            new FloatingActionButton(
              onPressed: _counterBloc.decrement,
              tooltip: 'Decrement',
              child: new Icon(Icons.remove),
            ),
          ])
        );
      }

      @override
      void dispose() {
        _counterBloc.dispose();
        super.dispose();
      }
      
    }

    We changed some few things in the UI:

    • Now we initialize the CounterBloc with initialCount = 0.
    • Then we removed the increment and decrement methods. Those method implementations are not the responsibility of UI anymore.
    • When the both FloatingActionButton is clicked, it calls the correspondent method in the CounterBloc.
    • Now we use StreamBuilder to show our data on the screen. We called StreamBuilder passing as Stream our counterObservable method available by the CounterBloc class, and we call the builder which must deal with the data which comes from the Strem and return the appropriate Widget.

    That's it, All done very well

     
0 Years in
Operation
0 Loyal
Clients
0 Successful
Projects

Words from our clients

 

Tell Us About Your Project

We’ve done lot’s of work, Let’s Check some from here