Hello again! 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.
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.
Looking at the image above, we can realize the flux. The Widgets send data/event to the BLoC class through Sink and are notified by Stream. See that there’s no business logic in the widget, that means what happened in BLoC is not the concern of UI. This architecture improves even easier tests, in which the business logic tests cases needed to be applied only to the BLoC classes.
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 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).
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);
/*but this listener below will print only the integer added after his initialization: 3, .../*subject.stream.listen(print);
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.stream.listen(print); // prints 3
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.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:
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:
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.
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.
At this moment our well-structured app will look like this:
Notes and conclusions:
That’s it, guys. There’s a lot of alternatives to structure your Flutter app and patterns to help with state management like BLoC, Redux, ScopedModel, and others. I confess BLoC is my favorite, but tell me if you liked it too. Thank you for reading the article so far, and please let your feedback. Tell me if you want part 2 with a more complex example.