Flutter State Management: BloC Pattern: Counter Tutorial

What is BloC Pattern and why use it?

Daniel Xav De Oliveira
3 min readNov 26, 2019

Firstly, the pattern can provide a solid architecture for our apps and there is a disadvantage to using the Set state as it will re-build the whole widget and its widget tree. This impacts performance.

Before continuing to BloC pattern it is important that one is familiar with the following :

Streams

Streams can be understood as a flow of data, and it can be analogised as a pipe of water , with an input flow and output flow

Stream Controller

The Stream Controller can be thought of as what controls the stream of data, and also included in the analogy as the tap that controls the flow of water in the pipe.

Sink

The sink inserts new data inside the stream. The sink also has a property called stream , that is used to output the data.

Subscriptions

The subscriptions monitors the stream to see if there is new data coming in or out the stream.

In general terms, data will be flowing from the BLOC to the UI or from UI to the BLOC in the form of streams

In the following tutorial, we’re going to build a Counter in Flutter using the Bloc library.

Setting up the Project

Let’s make a Flutter project named flutter_counter. Then go ahead and import in your pubspec.yaml the following packages:

flutter_bloc: ^2.0.0   
meta: ^1.1.6

The Bloc

Our counter app is just going to have two buttons to increment/decrement the counter value and a Text widget to display the current value. Let's get started designing the CounterEvents.

enum CounterEvent { increment, decrement }

import 'dart:async';
import 'package:bloc/bloc.dart';

import 'package:flutter/material.dart';

enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;

@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield state - 1;
break;
case CounterEvent.increment:
yield state + 1;
break;
}
}
}

The root: Main.dart

Now that we have our CounterBloc fully implemented, we can get started creating our Flutter application.

We are using the BlocProvider widget from flutter_bloc in order to make the instance of CounterBloc available to the entire subtree (CounterPage). BlocProvider also handles closing the CounterBloc automatically so we don't need to use a StatefulWidget

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_bloc.dart';
import 'counterpage.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider<CounterBloc>(
builder: (context) => CounterBloc(),
child: CounterPage(),
),
);
}
}

Counter Page

Finally, all that’s left is to build our Counter Page.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'counter_bloc.dart';

class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context);

return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Center(
child: Text(
'$count',
style: TextStyle(fontSize: 24.0),
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterBloc.add(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
counterBloc.add(CounterEvent.decrement);
},
),
),
],
),
);
}
}

We are able to access the CounterBloc instance using BlocProvider.of<CounterBloc>(context) because we wrapped our CounterPage in a BlocProvider. Furthermore, we are using the BlocBuilder widget from flutter_bloc in order to rebuild our UI in response to state changes (changes in the counter value).

That’s it! We’ve separated our presentation layer from our business logic layer. Our CounterPage has no idea what happens when a user presses a button; it just adds an event to notify the CounterBloc. Furthermore, our CounterBloc has no idea what is happening with the state (counter value); it's simply converting the CounterEvents into integers.

Thank you for reading! Feel free to make any other suggestions or recommendations for future challenges. Leave a few claps if you enjoyed it !

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Daniel Xav De Oliveira
Daniel Xav De Oliveira

Written by Daniel Xav De Oliveira

My aim is to document my journey as a Software Developer. Writing as I go along. To enforce new knowledge in my mind and to share with others !

Responses (1)

Write a response