How to Develop Your Flutter App With the BLoC Architecture
Get the answers to what is BLoC pattern in Flutter app development, what are the benefits, how to implement it, and how to use it in real-world projects.
Join the DZone community and get the full member experience.
Join For FreeFlutter is becoming more popular each day, though it’s a rather young technology. It first appeared in 2015 as Sky, and in 2017 it became the Flutter that we know and use. Flutter is backed by Google and allows developers to create beautiful and cost-effective cross-platform applications with a native feel.
There are many architectures you can use to build Flutter applications:
- Vanilla
- InheritedWidget
- ChangeNotifier + Provider
- BLoC
- MobX
- Redux
This freedom of choice can be rewarding, but it can also lead to inconsistent naming and bulky classes. In this article, I’ll be talking about the BLoC architecture, which is deemed one of the best choices for Flutter development by many programmers.
What is the BLoC architecture pattern?
BLoC stands for Business Logic Component, and as the name implies, it’s an architectural pattern that has separate components at its core. The BLoC architecture (which you can use by integrating the BLoC library into your project) was created by Felix Angelov and was introduced at Google I/O in 2019.
Note that BLoC is a pattern, not an architecture itself. You still need to organize data in your app according to an architecture like DDD, MVVM, or Clean.
BLoC helps with organizing data flows, and today it’s the most popular pattern for Flutter development. Let’s discover why.
Benefits of the BLoC architecture
The BLoC architecture has three core benefits. It is:
- Simple
- Testable
- Powerful
What makes it that way? The BLoC architecture allows developers to keep different layers of your application separate — namely the presentation and business logic layers. This makes it easy to test and reuse elements in different parts of your code.
The BLoC architecture also helps developers with state management, as they’re able to know an app’s state at any time. It also makes testing easier, as it’s much more convenient to write tests for specific parts of the code.
Separating the presentation from the business logic allows developers to reuse elements not only inside an app but across applications. Another advantage of the BLoC approach is that several team members can seamlessly work on a single code base. And because business logic is separate, it’s easier for developers to follow the same patterns.
The main principle of the BLoC architecture is creating complex products out of simple blocks. If you have a junior developer on your project, the BLoC architecture will make it easy for them to understand how everything works under the hood. Even for experienced developers, the BLoC architecture cuts the time needed to get acquainted with a project. This is especially important when working with tight deadlines and for commercial development.
Because this architecture keeps parts of the app small and separate, you can easily test each aspect of your application and know exactly what to fix.
Additionally, the BLoC architecture enforces a single way to change states across the whole app, making your app’s business logic predictable and consistent.
Now that we’ve discussed the advantages of the BLoC architecture, I’ll talk a bit about its logic.
How the BLoC Architecture Works
Before we delve into the logic of the BLoC architecture, let’s learn its main concepts.
- Events and actions are the inputs that appear when a user interacts with the UI: for example, swipes or scrolls.
- States are the reactions to these actions, and they change according to the event that a user initiates by interacting with the interface.
- A BLoC is a component that’s responsible for the business logic. It converts events into states and is the processing element that receives information and responds accordingly.
- A stream is an asynchronous flow of data that the user interface (UI) and BLoC react to.
The logic of the BLoC architecture is the following: when a user performs an action by interacting with the UI, the information about this action goes to the BLoC component. Then the BLoC component processes and interprets this information and responds by changing the state of the UI component.
Integrating the BLoC Architecture Step-By-Step
You first need to organize a Flutter app according to an architectural pattern like DDD, MVVM, or Clean. The BLoC architecture then works more like a pattern that further organizes the data flows in your app.
After you set up your architecture, you need to integrate the BLoC element into it.
Step 1: Add the BLoC Architecture To Your Project
To implement the BLoC architecture, you need to integrate the BLoC library into your project. To do this, you need to add the flutter_bloc: ^2.0.1 dependency to your pub spec.yaml file. Congrats! Now you have a Flutter package that will allow you to implement the BLoC pattern.
Step 2. Set Up Widgets In the BLoC Library
There are three main widgets in the BLoC library:
- Bloc
- BlocBuilder
- BlocProvider
You’ll need them to establish BLoCs, build those BLoCs according to the changes in the app’s state, and set up dependencies. Let’s see how to implement each widget and use it in your app’s business logic.
Bloc
The Bloc widget is the basic component you’ll need to implement all business logic. To use it, extend the Bloc class and override the mapEventToState and initialState methods.
In mapEventToState, you’ll need to handle arguments that represent actions. After you do that, you’ll need to return each argument as a state. Here’s an example:
xxxxxxxxxx
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc {
int get initialState => 0;
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, here you get the CounterEvent and handle it depending on the event type. The state (an int in this example) is then returned.
If you want to customize the response, you can create an abstract state or event:
x
//Customized state @immutable abstract class IncomingState {}
class InitialIncomingState extends IncomingState {}
class HandledState extends IncomingState {
final int counter;
HandledState(this.counter); } abstract class IncomingEvent {}
class IncrementEvent extends IncomingEvent {
IncrementEvent(); }class DecrementEvent extends IncomingEvent {
DecrementEvent(); }
BlocBuilder
BlocBuilder is a widget that responds to new states by building BLoCs. This widget can be called multiple times and acts like a function that responds to changes in state by creating widgets that then show up as new UI elements.
To get a BLoCs as a single widget that won’t be accessible via the BlocProvider and BuildContext, you need to specify the bloc like so:
xxxxxxxxxx
BlocBuilder(
bloc: blocA, // provide the local bloc instance builder: (context, state) {
// return widget here based on the state of BlocA} )
As you can see, you need to provide an extended Bloc class in the bloc argument. The instances of your state classes will appear in the BlocBuilder. Remember that the first state is the one that was previously created in the initialState method.
To avoid memory leaks, you shouldn’t create an instance of the Bloc class when creating a BlocBulider class. If you do, you won’t be able to close streams in the Bloc class. My advice is to create a Bloc instance in the initState method and close it with blocA.close() in the dispose method.
BlocProvider
This widget works as a dependency injection, meaning it can provide BLoCs to several widgets at a time that belong to the same subtree. BlocProvider is used to build blocs that will then be available for all widgets in the subtree. And because BlocProvider builds blocs, it’s also able to close them.
xxxxxxxxxx
BlocProvider(
builder: (BuildContext context) => BlocA(), child: ChildA(), );
Note that you can also use BlocProvider to provide a bloc you already have with a new tree of widgets. In this way, you can expand the capabilities of an existing bloc instead of creating a new one. However, in this case, BlocProvider won’t close the bloc because it didn’t create it.
Step 3. Create an Event
To perform any action with data — handle it, send it via the internet, save it to the database — you need to create an event in your Bloc component. To do this, you just need to call this method:
xxxxxxxxxx
bloc.add(YourEvent());
That’s it! Now the Bloc component will be able to handle your event.
As you can see, setting up your BLoC architecture pattern is very easy with the BLoC library.
Final Thoughts
The BLoC pattern is one of the best ways to enhance and organize your Flutter app’s architecture. It’s easy to set up and use, and it makes your code predictable and easy to test.
To implement the BLoC pattern in your Flutter app, just integrate the BLoC library and then create and set up the necessary widgets that will dictate your app’s business logic and behavior. Finally, use events to manipulate the data in your app and set up the connection between the actions and results.
I recommend the BLoC architecture for any Flutter project, as it makes code maintainable and helps you get rid of boilerplate and spaghetti code by keeping each part of your app separate and conveniently organized.
Published at DZone with permission of Svetlana Cherednichenko. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments