Effective BLoC pattern

Sagar Suri
Apr 27 · 6 min read

Hey Folks, Its been so long I have written anything about Flutter. After writing two articles on BLoC pattern I was spending time doing analysis on the usage of this pattern by the community and after answering some questions on the implementation of BLoC pattern I saw that there was a lot of confusion among people. So I came up with a list of “Rules of thumb” that can be followed to properly implement the BLoC pattern which will help a developer to avoid making common mistakes while implementing it. So today I present to you a list of 8 golden points that must be followed when working with BLoC.

Prerequisites

The audience I expect should know what BLoC pattern is or have created an app using the pattern(at least did CTRL + C and CTRL + V). If this is the first time you heard the word “BLoC” then the below three articles would be the perfect place to start understanding this pattern:

  1. Architect your Flutter project using BLoC pattern PART 1 and PART 2
  2. When Firebase meets BLoC pattern

Story of those who encountered BLoC

I know I know it is a tough pattern to understand and implement. I have seen many posts from developers asking “Which is the best resource to learn BLoC pattern?” After going through all the different posts and comments I feel the following points are the common hurdles every single person went through when understanding this pattern.

  1. Thinking reactively.
  2. Struggling to understand how many BLoC files need to be created.
  3. Scared whether this will scale or not.
  4. Don’t know when the streams will get disposed.
  5. What is the full form of BLoC? (It’s Business Logic Component 😅)
  6. Many more….

But today I will list down some of the most important points that will help you implement BLoC pattern confidently and efficiently. Without any further delay let’s look at those amazing points.

Every screen has its own BLoC

This is the most important point to remember. Whenever you create a new screen e.g Login screen, Registration screen, Profile screen etc which involves dealing with data, you have to create a new BLoC for it. Don’t use a global BLoC for all the screens in your app. You must be thinking that if I have a common BLoC I can easily use the data between the two screens. That’s not good because your repository should be responsible for providing those common data to the BLoC. BLoC will just take that data and provide to your screen in a manner which can be displayed to the user.

The left diagram is the correct pattern

Every BLoC must have a dispose() method

This is pretty straight forward. Every BLoC you create should have a dispose() method. This is the place where you do the cleanup or close all the streams you have created. A simple example of the dispose() method is shown below.

Don’t use StatelessWidget with BLoC

Whenever you want to create a screen which will pass data to a BLoC or get data from a BLoC always use StatefulWidget . The biggest advantage of using StatefulWidget over StatelessWidget are the lifecycle methods available in the StatefulWidget. Later down the article, we will talk about the two most important methods to override when working with BLoC pattern. StatelessWidgets are good to make a small static part of your screen e.g showing an image or hardcoded text. If you want to see the implementation of BLoC pattern in a StatelessWidget check out PART1 and in PART2 I showed why I converted from StatelessWidget to a StatefulWidget.

Override didChangeDependencies() to initialise BLoC

This is the most crucial method to override in a StatefulWidget if you need a context at the beginning to initialise a BLoC object. You can think of it as the initializing method(preferred for BLoC initialisation only). You may argue that we even have a initState() so why use didChangeDependencies() . As per the doc it’s clearly mentioned that it is safe to call BuildContext.inheritFromWidgetOfExactType from didChangeDependencies() method. A simple example of how to use this method is shown below:

Override dispose() method to dispose BLoC

Just like there is an initializing method, we have been provided with a method where we can dispose of the connections we created in the BLoC. The dispose() method is the perfect place to call the BLoC dispose() method associated with that particular screen. This method is always called when you leaving the screen(technically when the StatefulWidget is getting disposed). Below is a small example of that method:

Use RxDart only when dealing with complex logic

If you have worked with BLoC pattern earlier then you must have heard about RxDart library. It is a reactive functional programming library for Google Dart. This library is just a wrapper over the Stream API provided by Dart. I would advise you to use this library only when you are dealing with complex logic like chaining multiple network requests. But for simple implementations use the Stream API provided by the Dart language as it is quite mature. Below I have added a BLoC which uses Stream API rather than the RxDart library because the operations are quite simple and I didn’t need an additional library to do the same:

Use PublishSubject over BehaviorSubject

This point is more specific for those using the RxDart library in their Flutter project. BehaviorSubject is a special StreamController that captures the latest item that has been added to the controller, and emits that as the first item to any new listener. Even if you call close() or drain() on the BehaviorSubject it will still hold the last item and emit when subscribed. This can be a nightmare for a developer if he/she is not aware of this feature. Whereas PublishSubject doesn’t store the last item and is best suited for most of the cases. Check out this project to see the BehaviorSubject feature in action. Run the app and go to the “Add Goal” screen, enter the details in the form and navigate back. Now again if you visit the “Add Goal” screen you will find the form pre-filled with the data you entered previously. If you are a lazy person like me then look at the video I have attached below:

Proper use of BLoC Providers

Before I say anything about this point do check the below code snippet(line 9 and 10).

You can clearly see that multiple BLoC providers are nested. Now you must be worried that if you keep adding more BLoCs in that same chain then it will be a nightmare and you will conclude that BLoC pattern cannot scale. But let me tell you that there can be a special case(a BLoC only holding the UI configurations which is required across the app) when you need to access multiple BLoCs anywhere down the Widget tree so for such cases the above nesting is completely fine. But I would recommend you to avoid such nesting most of the time and provide the BLoC from where it is actually needed. So for example when you are navigating to a new screen you can use the BLoC provider like this:

This way the MovieDetailBlocProvider will provide the BLoC to the MovieDetail screen and not to the whole widget tree. You can see that I stored the MovieDetailScreen in a new final variable to avoid recreation of the MovieDetailScreen every time when the keyboard is opened or closed in the MovieDetailScreen.

This is not the end

So here we come to the end of this article. But this is not the end of this topic. I will keep adding new points to this ever-growing list of optimizing the BLoC pattern as I learn better ways of scaling and implementing the pattern. I hope these points will surely help you implement BLoC pattern in a better way. Keep learning and keep coding. :) If you liked the article then show your love by hitting 50 claps 😄 👏 👏.

Having any doubt, connect with me at LinkedIn or follow me at Twitter. I will try my best to solve all your queries.

Check out my other articles

FlutterPub

The Pub(lication) for all about the Flutter and its magic

Sagar Suri

Written by

Google certified Android app developer | Flutter Developer | Computer Vision Engineer | Gamer

FlutterPub

The Pub(lication) for all about the Flutter and its magic