Bloc Pattern in Flutter | Part 1šŸ’™

Do you feel Bloc is tough? Me too!

Abhishek Doshi
6 min readDec 20, 2020

Letā€™s make it easy then!šŸ˜

In this article, we will check out how to use Bloc in Flutter using flutter_bloc package with an example.

Package Link: flutter_bloc | Flutter Package (pub.dev)

So Bloc basically uses the concept of Inherited Widget. We can implement Bloc using Stream and Sink but, we already have a package flutter_bloc which is a wrapper of Stream and Sink.

Before we start bloc, there are two concepts that needs to be clear:

  • Events: Events are basically the functionalities of the app. Letā€™s say for our basic Counter app, Increment is a functionality and hence, one of our event is Increment.
  • States: State is the information that can be read synchronously when the widget is built and might change during the lifetime of the widget. Or, in simple words, we can say that the State is something that our app is, before and after the event. Hence, in our Counter example, the app before increment and after increment is State. Also, we can say that we have a state while itā€™s incrementing.

In BLOC, we give Event as input to BLOC. Then we do the processing/business logic in the Bloc and provide State as output.

In a Bloc, there will be 3 main files:

  • bloc file: This file contains the main Business Logic
  • events file: This file states all the events that are present in your app.
  • state file: This file contains all the states that your app undergoes.

Letā€™s start with the coding!

We will re-write the basic Counter App using BLOC!

Step 1: Import flutter_bloc in pubspec.yaml

flutter_bloc: ^6.1.1

Step 2: Create a folder named bloc and create 2 files in it. Here, we will name them as counter_bloc andcounter_event . As we donā€™t have multiple states, we wonā€™t create counter_state .

This is how the project structure will look like. You can name your files according to your comfort.

Step 3: Letā€™s first create the basic UI and functionality of our app by scratch without Bloc. Hereā€™s the main.dart and home.dart files:
main.dart:

import 'package:bloc_counter_example/home.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}

home.dart

import 'package:flutter/material.dart';class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int counter = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => setState(() {
counter++;
}),
),
appBar: AppBar(
title: Text('Bloc Counter Example'),
),
body: Center(
child: Text(
'$counter',
style: TextStyle(fontSize: 50.0),
),
),
);
}
}

So this is the basic application (Counter Application) that we created from scratch. Now, letā€™s work on our BLOC.

Step 4: Letā€™s create our events. As this is a simple example, we just have 1 event i.e. Increment and we donā€™t even need to pass any parameters. So we will use enum in this case. So, our counter_event.dart will look like this:

enum CounterEvent { increment }

Step 5: Now, letā€™s work on our bloc file i.e. counter_bloc.dart .
In this file, we need to specify our business logic. We have to create a class that extends Bloc class. Let me show you how it will look:

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'counter_event.dart';
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.increment:
yield state + 1;
break;
default:
break;
}
}
}

So, the above code is our counter_bloc.dart . This is the main core of our application that will increment the counter. Letā€™s break this code and understand:

  • class CounterBloc extends Bloc<CounterEvent, int>
    Here, CounterEvent is our event i.e. enum that we created in counter_event.dart and int is the state because we are simply incrementing an integer variable. If your application is complex, instead of int, you can specify your State class.
  • CounterBloc() : super(0);
    So, this is basically the constructor of CounterBloc() class where we pass an initial value i.e. 0 in our case to the parent class i.e. Bloc class because our CounterBloc extends Bloc.
  • Then we need to implement one method named mapEventToState . This method basically maps our input events with the corresponding output states.
    Stream<int> mapEventToState(CounterEvent event) async*
    This method takes Event as input and returns a Stream of state and hence we need to make it async*. Now, if you have custom state file, you have to replace int with your state class.
  • Now, we check for the events. If itā€™s CounterEvent.increment, we need to increment the value. yield adds a value to the output stream of the surrounding async* function. It's like return, but doesn't terminate the function.
switch (event) {
case CounterEvent.increment:
yield state + 1;
break;
default:
break;
}

Here, we complete our counter_bloc.dart file implementation!

So, our BLOC is ready! Now we just need to integrate it with our UI i.e. main.dart and home.dart files

I hope it was pretty clear till here! Donā€™t bang your laptop, we are just a few lines of code away šŸ˜‰

Step 6: Open main.dart file and wrap the child of MaterialApp with BlocProvider. Your file will look like this:

import 'package:bloc_counter_example/bloc/counter_bloc.dart';
import 'package:bloc_counter_example/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider<CounterBloc>(
create: (context) => CounterBloc(),
child: MyHomePage(),
),
);
}
}

So, basically BlocProvider will provide the access to Bloc throughout the Widget Tree.

BlocProvider<CounterBloc>(
create: (context) => CounterBloc(),
child: MyHomePage(),
),

Here, CounterBloc is our bloc class that we created in previous step. We need to provide which bloc is to be created (here CounterBloc()) and the child where the bloc should be accessible. Yep! Thatā€™s it for main.dart file. Letā€™s now move to home.dart šŸ˜

Step 7: Open home.dart file and create an instance of CounterBloc()

CounterBloc _counterBloc;

Now, inside our build method, letā€™s instantiate our _counterBloc variable.

_counterBloc = BlocProvider.of<CounterBloc>(context);

What does the above statement does??? It actually gives you access to the CounterBloc that we created. Now, using _counterBloc we can access our event and state.

Step 8: Now, wrap the Text() widget (where we display the count) with BlocBuilder

body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, state) {
return Text(
'$state',
style: TextStyle(fontSize: 50.0),
);
},
),
),

Here, the state variable that we can see, has access to the states. In this case, state is just an integer value which will get incremented. So we can directly display the state. Now, letā€™s change our Floating Action Button onPressed() method!

Step 9: In onPressed for Floating Action Button, we need to add the event to the bloc. Syntax:

onPressed: () => _counterBloc.add(CounterEvent.increment),

This is how your home.dart file will look now:

import 'package:bloc_counter_example/bloc/counter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/counter_event.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CounterBloc _counterBloc;
@override
void dispose() {
_counterBloc.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
_counterBloc = BlocProvider.of<CounterBloc>(context);
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _counterBloc.add(CounterEvent.increment),
),
appBar: AppBar(
title: Text('Bloc Counter Example'),
),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, state) {
return Text(
'$state',
style: TextStyle(fontSize: 50.0),
);
},
),
),
);
}
}

And done!!!!!!!!!!!!!

We just implemented Bloc in our basic Counter ApplicationšŸ’™

In the next article, we will take a complex application with multiple states and implement it using Bloc.

Hope you enjoyed this article!

GitHub Repository: AbhishekDoshi26/bloc_counter_example (github.com)

If you loved it, you can Buy Me A Coffee!

Donā€™t forget to connect with me on:

Donā€™t stop, until you are breathing!šŸ’™
- Abhishek Doshi

--

--

Abhishek Doshi

Google Developer Expert ā€” Dart, Flutter & Firebase šŸ’™šŸ’›