Good architecture is very important in every good building as well as a good application. When building an application without proper architecture you will end up in a state where everything is messed up. If you don’t follow a proper architecture for your application you will lose control of your application like maintainability, testability, and scalability. In this guide, we will talk about Blocs. Bloc is not just a state management system, it’s also an architectural design pattern that helps you to build production-level applications.

What is Flutter Bloc?

Flutter Bloc is a state management library for Flutter. It is a reactive state management library that helps in separating the business logic from the presentation layer of an application. This makes your code easier to read, understand and debug.

Why Flutter Bloc?

There are many state management solutions and deciding which one to use can be a daunting task. There is no one perfect state management solution! What’s important is that you pick the one that works best for your team and your project.

  • It is easy to learn and use. The Flutter Bloc documentation is clear and concise and there are many tutorials available online.
  • It is powerful and scalable. Flutter Bloc can handle complex applications with a large number of states.
  • It is reactive. Flutter Bloc updates the state of the application automatically in response to events.
  • It is well-tested and maintained. The Flutter Bloc team is committed to providing a stable and reliable library.

Flutter Bloc Architecture

Flutter Bloc

Using the bloc library allows us to separate our application into three layers:

  • Presentation
  • Business Logic
  • Data

-> Repository

 

-> Data Provider

 

UI (Presentation)

The presentation layer in Flutter is the layer that is responsible for rendering the UI and responding to user input. It is the layer that is closest to the user and is responsible for making the app look and feel good.

The presentation layer is made up of widgets, which are small, reusable components that can be combined to create complex interfaces. Widgets are responsible for drawing themselves on the screen and responding to user input.

Bloc (Business Logic Layer)

The business logic layer is the bridge between the user interface (presentation layer) and the data layer. The business logic layer is notified of events/actions from the presentation layer and then communicates with the repository to build a new state for the presentation layer to consume.

Copy to Clipboard

Data Layer

This layer is the lowest level of the application and interacts with databases, network requests, and other asynchronous data sources.

The data layer can be split into two parts:

  • Data Provider
  • Repository

Data Provider

The data provider is a layer that provides access to data. It can be used to store data in memory, in a file, or a database. It can also be used to fetch data from an external source, such as a web service.

There are many different types of data providers in Dart. Some of the most common types include:

In-memory Data Providers

These providers store data in memory. They are easy to use and fast, but they can only store a limited amount of data.

File Data Providers

These providers store data in files. They can store a large amount of data, but they are not as fast as in-memory providers.

Database Data Providers

These providers store data in a database. They can store a very large amount of data and are very fast, but they can be more complex to use than other types of providers.

Web Service Data Providers

These providers fetch data from an external web service. They are easy to use and can be used to fetch data from any web service, but they can be slower than other types of providers.

Copy to Clipboard

Repository

This layer contains one or more than one Data Providers. Actually, the repository layer is a wrapper around one or more data providers with which the Bloc Layer communicates.

 

Copy to Clipboard

Core Concepts of Flutter Bloc:

Streams

A stream is a sequence of data elements that are continuously generated or received over time.If you’re unfamiliar with Streams just think of a pipe with water flowing through it. The pipe is the Stream and the water is the asynchronous data.

Events

Events are often used to trigger actions in a computer program. For example, when a user clicks a button in UI, an event is generated. This event can then be used to call a function or to change the state of the program.

State

The state is updated whenever an event is dispatched. The BLoC then emits the new state to all of its subscribers. This allows the subscribers to update their UI in response to changes in the state of the application.

Let’s Start with Coding

Project Overview

In this tutorial, we are going to build a weather application (following) flutter bloc. Our weather app will pull live weather data from the public OpenMeteo API and demonstrate how to separate our application into layers (data, repository, business logic, and presentation).

Step 1: Create a Flutter Project

Open the terminal and write the command

Copy to Clipboard

Step 2: Package We are using

Go to pubspec.yaml and add flutter_bloc, bloc, http, equatable,hydrated_bloc, and path_provider packages inside dependencies. Equatable overrides == and hashCode for you so you don’t have to waste your time writing lots of boilerplate code.

 

Copy to Clipboard

Step 3: Project Setup

main.dart
Copy to Clipboard

app.dart

Copy to Clipboard

weather_screen.dart

Copy to Clipboard

WeatherEmpty

Copy to Clipboard

WeatherError

Copy to Clipboard

WeatherLoading

Copy to Clipboard

weather_populated.dart

Copy to Clipboard

screen_search.dart

Copy to Clipboard

Final UI:

Data Layer

Data: Retrieve raw weather data from the API

We’ll be focusing on two endpoints:

  • https://geocoding-api.open-meteo.com/v1/search?name=$city&count=1 to get a location for a given city name
  • https://api.open-meteo.com/v1/forecast?latitude=$latitude&longitude=$longitude&current_weather=true to get the weather for a given location

Open https://geocoding-api.open-meteo.com/v1/search?name=chicago&count=1 in your browser to see the response for the city of Chicago. We will use the latitude and longitude in the response to hit the weather endpoint.

The latitude/longitude for Chicago is 41.85003/-87.65005. Navigate to https://api.open-meteo.com/v1/forecast?latitude=43.0389&longitude=-87.90647&current_weather=true in your browser and you’ll see the response for weather in Chicago which contains all the data we will need for our app.

Repository Layer

The goal of our repository layer is to abstract our data layer and facilitate communication with the bloc layer. In doing this, the rest of our code base depends only on functions exposed by our repository layer instead of specific data provider implementations. This allows us to change data providers without disrupting any of the application-level code.

weather_repository.dart

Here we just declared the method

Copy to Clipboard

 

Now Implement the WeatherRepository abstract class on weather_repository_impl

Copy to Clipboard

Model

CurrentWeather.dart

We are using three model current_weather.dart, location.dart and weather.dart

Copy to Clipboard

Location.dart

Copy to Clipboard

weather.dart

Copy to Clipboard

Business Logic Layer

Create bloc files

  • Install bloc plugin in your IDE
  • Restart your IDE
  • Go to File -> New -> Bloc Class
  • Enter your bloc name that you want then you see in the lib directory the name bloc is created and it has three different file wather_bloc.dart, weather_event.dart and weather_state

Bloc: The weather_bloc.dart class is a bridge between our UI and the Data layer(weather_repository_impl.dart), In other words, this class will handle all the Events triggered by the User and send the relevant State back to the UI.

We are extending our WeatherBloc class with Bloc which takes two things WeatherEvent and WeatherState. As the name suggests, they handle the applications State and Events respectively.

These two classes are implemented in the weather_event.dart and weather_state.dart respectively.

Weather_bloc.dart

Copy to Clipboard

 

_getWeather(String? city) uses our weather repository to try and retrieve a weather object for the given city

Event: weatherEvent.dart

Copy to Clipboard

 

  • In this class, we define different kinds of events by extending the abstract event class.
  • For example, when the user presses the search icon, the WeatherRequest event is triggered.
  • Here I’ve also created a final field called city. It’s nothing but a string inputted by the user in the search text field.
  • We have to pass it to the event in order to access it in our bloc.
  • Now let’s implement weather_state.dart class

State:
Weather_state.dart

 

Copy to Clipboard

 

In this class, we define only one state but if you want then you create a abstract class like this abstract class WeatherState {} and extend this class like

class WeatherInitial extends WeatherState {}

class WeatherLoadInprogress extends WeatherState {}

There are four states our weather app can be in:

  • Initial before anything loads
  • Loading during the API call
  • Success if the API call is successful
  • Failure if the API call is unsuccessful

The WeatherStatus enum will represent the above.

How To Access bloc?

Trigger an Event:

You need a context bloc and event that you trigger. In this WeatherBloc is our bloc and GetWeather is our event for getting weather and it needs a field city. We input the city dhaka.

Copy to Clipboard

BlocProvider

  • BlocProvider widget provides a bloc to its children (i.e Widgets).
  • BlocProvider is used as a dependency injection (DI) widget so that a single instance of a bloc can be provided to multiple widgets within a subtree.
  • Okay. So now we know what BlocProvider does, where can we put it?
  • You put the BlocProvider the root MaterialApp or Every single Widget. Here we provide blocProvider in every widget
Copy to Clipboard
  • Now you can access the WeatherBloc

MultiBlocProvider

  • Bloc provides us with a MultiBlocProvider widget that takes a List of Bloc and provides it to its children. Let me demonstrate.

 

Copy to Clipboard

BlocBuilder

  • BlocBuilder is a widget that helps Re-building the UI based on State changes.
  • In our case we want our UI to update the state when the user presses the Get Weather button.
  • BlocBuilder builds the UI every single time state changes
  • So, it’s very necessary to place BlocBuilder around the Widget that we want to rebuild.
  • You can also wrap the whole Widget inside the BlocBuilder (i.e around the Scaffold), but it’s not a good way. Because think about the time and processing power that will be consumed when your whole widget tree rebuilds just to update a Text widget inside the tree. So make sure you wrap the BlocBuilder around the widget that needs to be rebuild when the state changes.
Copy to Clipboard

 

  • Only specify the bloc if you wish to provide a bloc that will be scoped to a single widget and isn’t accessible via a parent BlocProvider and the current BuildContext.
  • Build When parameter takes the previous bloc state and current bloc state and returns a boolean. If buildWhen returns true, builder will be called with state and the widget will rebuild. If buildWhen returns false, builder will not be called with state and no rebuild will occur. Actually it prevent unexpected build.

 

Copy to Clipboard

BlocSelector

  • Unnecessary builds are prevented if the selected value does not change.
  • The selected value must be immutable in order for BlocSelector to accurately determine whether builder should be called again.

 

Copy to Clipboard

BlocListner

  • As the name suggests, this will listen to any state change as BlocBuilder does.
  • But instead of building the widget like BlocBuilder, it takes one function, listener, which is called only once per state, not including the initial state.
  • It also has a bloc parameter. Only specify the bloc if you wish to provide a bloc that is otherwise not accessible via BlocProvider and the current BuildContext.
  • The listenWhen parameter is the same as BlocBuilder’s buildWhen but for Listener.
  • The whole idea of BlocListener is – It is not responsible for building/updating the widget like BlocBuilder does.
  • It only listens to the state changes and performs some operation. The operation could be (Navigating to other screens when state changes, Showing Snackbar on a particular state, etc).

 

Copy to Clipboard

MultiBlocListener

  • MultiBlocListener is a Flutter widget that merges multiple BlocListener widgets into one. MultiBlocListener improves the readability and eliminates the need to nest multiple BlocListeners

 

Copy to Clipboard

BlocConsumer:

  • Bloc provides a BlocConsumer widget, which combines both BlocListener and BlocBuilder.
  • An optional listenWhen and buildWhen can be implemented for more granular control over when the listener and builder are called.

 

Copy to Clipboard

RepositoryProvider:

  • It is the same widget as BlocProvider.
  • But the main difference is BlocProvider provides a single instance of bloc to its children whereas RepositoryProvider provides repositories to its children.
  • It is used as a dependency injection (DI) widget so that a single instance of a repository can be provided to multiple widgets within a subtree.

 

Copy to Clipboard

 

  • Retrieve the Repository

 

Copy to Clipboard

MultiRepositoryProvider:

  • MultiRepositoryProvider is a Flutter widget that merges multiple RepositoryProvider widgets into one. MultiRepositoryProvider improves the readability and eliminates the need to nest multiple RepositoryProvider. By using MultiRepositoryProvider we can go from:

 

Copy to Clipboard

Wrapping Up

Bloc patterns can be challenging to understand at first, but it will become easier with practice. The best way to learn is by building small applications. I hope this article has helped you to understand the basics of Bloc. If you have any questions or feedback, please leave a comment below.

Here are some additional tips for learning Bloc:

  • Start with a simple application.
  • Break the application down into small, manageable pieces.
  • Use the Bloc library documentation to help you get started.
  • Don’t be afraid to ask for help. There are many resources available online and in the Flutter community.

With a little practice, you’ll be using Bloc like a pro in no time!