codete getting started with flutter bloc here s all you need to know main e256d21110
Codete Blog

Getting Started with Flutter BLoC? Here's All You Need to Know

codete logo 41a83d4d26

20/07/2021 |

12 min read

Codete Team

Flutter, a cross-platform application framework supported by Google, allows developers to produce cost-effective applications with a native look and feel. This neat effect could be achieved with several architectures, to name a few: Vanilla, ChangeNotifier + Provider, MobX, Redux, or BLoC.

Such freedom of choice may, however, lead to class names and large classes being inconsistent. In this article, we will examine BLoC architecture, which is considered one of Flutter's finest solutions.

Table of contents:

  1. What is the BLoC pattern?
  2. How does the BLoC work?
  3. Flutter BLoC - getting started
  4. Learning materials
  5. Flutter BLoC - notes to remember

 

What is the BLoC pattern?

The BLoC – an acronym for Business Logic Component – is currently one of the most popular Flutter architectures. It was first mentioned by Google at the Google I/O in 2018 and has since then become the standard for many developers when it comes to state management solutions in Flutter app development.

BLoC is simple and elegant, making it easy to understand for both novice and experienced Flutter developers. The BLoC architecture's strength comes from its ability to build complex applications from smaller components. Furthermore, because these small components are discrete, it is simple to test any aspect of an application.

Generally speaking, BLoC is a design pattern based on separate components, allowing for the creation of complex user interfaces by assembling these widgets. It also seeks to reuse the same logic across several widgets. Due to Flutter's ability to manage the widget status (setState function), a widget will be automatically redrawn/rendered when the widget status changes. Plus, Flutter includes the setState function for changing or redrawing the state. 

BLoC tries to make state changes predictable by regulating when they can happen and enforcing a single way to change state across an entire application. BLoC components only contain business logic that can easily be shared between various Dart applications (which you can use with your project by incorporating the BLoC library).

Anyway, what is the purpose of an app's architecture?

Once you start a project, you never know how big it will eventually become. In my opinion, it is best to take the safe route and design the architecture cleanly from the start, as this can save a lot of trouble later on. 

What exact situations are we talking about?

  • When mobile app projects get big enough, it's more likely that screens (or widgets in Flutter) share data across the app. It becomes cumbersome to always pass this information down in the constructor and along with it a callback function that is called when the nested widget registers a user interaction which in response is expected to change that state.
  • The separation of view and logic is also an issue when developing software without a structuring pattern. This becomes a huge issue as your app grows. Imagine a screen with a header, a list of elements, and an input field at the bottom. One day your client asks you to add another screen that is similar (in appearance) but not identical (in behavior). Still, the behavior and visuals are tightly coupled, making reusing it impossible.
  • For widgets that rely on BLoC classes, you can write widget tests that mock the BLoC class (returning a state) and make assumptions about the widget tree. This way you can verify that the BLoC class inner logic works and the widget reacts to the BLoC state correctly. Finally, you can write integration tests that mock BLoCs, preventing them from making actual HTTP calls or accessing data layers that aren't available during testing.

When a project has deteriorated to the point where it is no longer manageable, it takes a significant amount of effort to transform it into a well-structured one. Sadly, I've witnessed companies deciding to rewrite everything because the effort of a massive refactoring would simply exceed the effort of a complete rewrite. 

That’s why I always recommend thinking about the architecture in case of apps that require scalability, readability, or maintainability and are worked on by more than one person. Theoretically, you can save yourself the hassle and skip it for small apps and personal projects (if you are sure that your pet project won’t grow with time…could one ever be?). 

How does the BLoC work?

When user clicks and interacts with the UI, the BLoC component receives an action or an event. The BLoC component's primary responsibility is to interpret the action, handle it, and return a new or updated state to the UI component.

Technically, BLoC is based on streams. A stream is essentially a metaphor for data flowing  asynchronously from an issuer. This data stream can be listened to. The issuer determines when to emit a new event that alerts all listeners. Although technically incorrect, imagining a stream as a sequence of Futures may be useful.

Using the BLoC design, you can separate different layers of your application, particularly the display and business logic layers. The streams and reactivity of Flutter make it an obvious choice for creating maintainable and testable applications. In other words, by using this design, you can easily test any feature of your application and know exactly what needs to be fixed. 

So, the main idea behind BLoC is that a widget always knows the state of the BLoC (or BLoCs) on which it is currently reliant. When the widget wishes to change the state (let’s say, in response to the user's action), it notifies the BLoC that something has occurred (“login was triggered”). The BLoC responds asynchronously to its defined logic and emits n states (“loading”, “login successful”). When a BLoC state is emitted, the parts of the widget that are dependent on that particular BLoC state are updated. This is a recurring circle.

As a result, the view layer (consisting of widgets) never communicates directly with the data layer (consisting of API calls or database queries). Instead, it delegated this responsibility to the BLoC layer, which serves as a go-between for the two. This architecture also ensures that all app states can be changed in the same way, resulting in predictable and consistent business logic.

Furthermore, the BLoC architecture facilitates the development of app state management by allowing developers to determine the app's current state at any point in time. Aside from that, writing tests for specific sections of code is far more convenient.

As you can see, the central tenet of the BLoC architecture is to build complicated things out of simple pieces. Any junior developer on your team can easily understand the inner workings of BLoC architecture. This architecture shortens the time it takes even experienced developers to become acquainted with a project. This is especially important in commercial development and projects with short deadlines.

Flutter BLoC - getting started

Complex programs with multiple setState calls, user interface-intensive functionality (Widgets), and repeatedly redrawn widgets can cause performance issues. BLoC allows us to decouple our functionality from the user interface while still adhering to the flutter reactive paradigm of redrawing/rendering when a state, or in this case, a new stream, is detected by flutter.

Still, the BLoC pattern is not a stand-alone architecture. You'll still need to organize the data in your app using a DDD, MVVM, or Clean design.

Incorporate the BLoC Architecture into Your Project

First things first, let’s start with incorporating the BLoC library into your project in order to implement the BLoC architecture. To accomplish this, add the flutter bloc: 2.0.1 dependency to your pub spec.yaml file. Done and done. You now have a Flutter package that allows you to use the BLoC pattern.

Upgrade widgets to BLoC Library

In the BLoC library there are three main widgets: Bloc, BlocBuilder, and BlocProvider. It is necessary for you to build those BLoCs based on changes in the state of the app and set up all the dependencies. Let's see how to implement and use each widget in the business logic of your app.

Bloc

The Bloc widget is the fundamental component required to implement all business logic. Extend the Bloc class and override the mapEventToState and initialState methods to use it. You'll need to handle arguments that represent actions in mapEventToState. After that, you must return each argument as a state.

enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc {
@override int get initialState => 0;
@override Stream mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield state - 1; break; case CounterEvent.increment:
yield state + 1; break; } } }

As you can see, you get the CounterEvent here and handle it based on the type of event. The state (in this case, an int) is then returned. You can customize the response by creating an abstract state or event:

//Customized state @immutable abstract class IncomingState {}
class InitialIncomingState extends IncomingState {}
class HandledState extends IncomingState {
final int counter;
HandledState(this.counter); }@immutable abstract class IncomingEvent {}
class IncrementEvent extends IncomingEvent {
IncrementEvent(); }class DecrementEvent extends IncomingEvent {
DecrementEvent(); }

BlocBuilder

BlocBuilder is a widget that responds to new states by constructing BLoCs. This widget can be called multiple times and functions as a function, responding to changes in state by creating widgets that then appear as new UI elements. To get BLoCs as a single widget that is not accessible via the BlocProvider and BuildContext, specify the bloc as follows:

BlocBuilder(
bloc: blocA, // provide the local bloc instance builder: (context, state) {
// return widget here based on the state of BlocA} )

As you can see, the bloc argument requires an extended BLoC class. In the BlocBuilder, you will see instances of your state classes. Remember that the first state was created previously in the initialState method.

You should not create an instance of the Bloc class when creating a BlocBuilder class to avoid memory leaks. You will be unable to close streams in the Bloc class if you do so. My recommendation is to create a Bloc instance in the initState method and close it in the dispose method with blocA.close().

 

BlocProvider

This widget is a dependency injection widget, which means it can provide BLoCs to multiple widgets in the same subtree at the same time. BlocProvider is used to create blocs that are then accessible to all widgets in the subtree. BlocProvider can also close blocs because it creates them.

BlocProvider(
builder: (BuildContext context) => BlocA(), child: ChildA(), );

It should be noted that BlocProvider can also be used to provide a new tree of widgets to an existing bloc. Instead of creating a new bloc, you can expand the capabilities of an existing one. However, because it did not create the bloc, BlocProvider will not close it in this case.

Make an Event

To handle data, send it over the internet, or save it to a database, you must first create an event in your Bloc component. To accomplish this, simply invoke the following method:

bloc.add(YourEvent());

That's all! Your event will now be handled by the Bloc component. As you can see, using the BLoC library makes it very simple to implement your BLoC architecture pattern.

More learning materials

Here are some tutorials that use the BLoC pattern (and the BLoC package) and demonstrate how to implement it in the context of real-world examples:

  • A general tutorial on the bloc pattern in action
  • A tutorial that expands on the previous one by including service communication
  • Weather - an example of how to create a Weather Application using the bloc and flutter_bloc packages. The app uses a RefreshIndicator to implement "pull-to-refresh" as well as dynamic theming.
  • I/O Photo Booth - an example of how to use the bloc and flutter_bloc packages to create a virtual photo booth web app -- made for Google I/O 2021.
  • Github Search - an example of how to create a Github Search Application using the bloc and flutter_bloc packages.
  • Fluttersaurus - an example of how to use the bloc and flutter_bloc packages to create a thesaurus app – made for Bytconf Flutter 2020.
  • Timer - an example of how to create a Timer using the bloc and flutter_bloc packages.

Flutter BLoC - notes to remember

The BLoC architecture is a versatile and easy-to-maintain pattern. This architecture allows you to create a reactive app using streams and sinks, so I recommend it for any Flutter project - to make your job easier, I named a few go-to types below.

Consider the use of the BLoC pattern:

  • When creating a StatefulWidget or writing setState ()
  • If your widget needs the data layer to communicate (e. g. a repository, a service, an API call)
  • When a widget logic is managed by the widget itself and you feel it's too complex
  • If you want to make the logic fully independent of the widget
  • If you want to automatically test the logic of your widget
  • When you start a project, you know that there will be more than a few screens and that it needs to be scalable and durable

If you want to implement the BLoC pattern in your Flutter app, simply integrate the BLoC library and create and set the widgets needed to define the business logic and behavior of your app. Always use events to manipulate the data in your app and connect the actions to the results. And one more thing. If possible, try to use ready-made solutions for your architecture (i.e.: Google Bloc library) to improve the efficiency of your development process even more. It will help you quickly set up your architecture and avoid spaghetti and boilerplate code in the future. 

I highly recommend BLoC architecture for any flutter project as it makes the code maintainable by keeping each part of the app separate and conveniently organized and helps you get rid of boiler and spaghetti code.

Rated: 5.0 / 1 opinions
codete logo 41a83d4d26

Codete Team

Codete is an IT consulting and software development company. Since 2010, we’ve been supporting businesses worldwide in gaining competitive advantage by means of modern technology. We advise on digitalization, develop and implement high-quality solutions, and augment our clients’ teams with skilled software developers.

Our mission is to accelerate your growth through technology

Contact us

Codete Global
Spółka z ograniczoną odpowiedzialnością

Na Zjeździe 11
30-527 Kraków

NIP (VAT-ID): PL6762460401
REGON: 122745429
KRS: 0000983688

Get in Touch
  • icon facebook
  • icon linkedin
  • icon instagram
  • icon youtube
Offices
  • Kraków

    Na Zjeździe 11
    30-527 Kraków
    Poland

  • Lublin

    Wojciechowska 7E
    20-704 Lublin
    Poland

  • Berlin

    Bouchéstraße 12
    12435 Berlin
    Germany