Flutter Bloc

Flutter Bloc:

The Bloc stands for Business Logic Component. it is a pattern created by Google and announced at Google I/O ‘. The BLoC pattern used to handle the flow of data within the app. You add streams of event/data input into a Sink and listen to them as streams of data output through a Stream.

Why Bloc?

Bloc used to separate your presentation from business logic, making your code fast, easy to test, and reusable.

While building production quality applications, managing different states become critical.

As a developer we want to:

  • To know what state our application is in at any point in time.
  • easily test every case to make sure our app is responding appropriately and properly.
  • To record every single user interaction in our application so that it helps to make data-driven decisions to grow business.
  • It is useful to work as efficiently as possible and reuse components both within our application and across other applications too.
  • To have many developers seamlessly working within a single code base following the same patterns and conventions.
  • Used to develop fast and reactive apps.

Bloc was designed to meet all of those needs and many more.

There are many state management solutions and deciding which one to use can be a daunting task.

Bloc was designed to help with the three core values in mind for developers:

  • Simple to understand
    • It is very easy to understand & can be used by developers with different varying skill levels.
  • Powerful for Developers
    • Helps to make amazing and complex applications by composing them of smaller components.
  • Testable
    • We can easily test every aspect of an application so that we can iterate with confidence.

Bloc pattern helps to make state changes predictable by regulating the application when a state change occurs and enforcing a single way to change state throughout an entire application.

 

Flutter Bloc Pattern Architecture:

Using Bloc pattern we can separate our application into three parts as shown below.

  • Data
  •          Data Provider
  •          Repository
  • Business Logic
  • Presentation

 

Data Layer:

the data layer is used to retrieve and manipulates the data from the sources it may be one or more than one. the data layer is divided into two parts as shown above and it interacts with the database and other network sources and asynchronous methods.

 

Flutter Bloc (Business Logic) Layer:

The bloc layer’s responsibility is to respond to events from the presentation layer with new states. The bloc layer sends a asyn request to the backend and depend on one or more repositories to retrieve data needed to build up the application state. The Bloc layer gets async response from the backend and sends it to the presentation layer in this way Flutter Bloc layer works.

Flutter Bloc Presentation Layer:

The presentation layer’s responsibility is to render the response from the Bloc layer itself based on one or more bloc states. In addition to this, it handles user input and application lifecycle events.

Core Concepts of Flutter Bloc:

To work with Flutter Blocs we need to understand the core concepts of Bloc before implementing it into the application.

  • bloc Events:
  • Bloc States
  • Bloc Transitions
  • Bloc Streams
  • Bloc Blocs
  • Bloc Delegates

Flutter Bloc Events:

Events are the user input to a Bloc. They are used to get a response to user interactions such as button presses or lifecycle events like page loads. While designing the app we need to define how users will interact with the application. When a user taps on one of the buttons, something will happen to notice the “brains” of our app so that it will respond to the user’s input, this is where the events come into play a  major role.

Flutter Bloc States:

States are the output of a Bloc and represent the part of your application’s state. UI components can be notified of states and redraw portions of themselves based on the current state.

To understand the state, go through this example. suppose if we are building a counter app. on this counter app, we have to events one is counterEvent.increment and other is counterEvent.decreament. Now here we have to define the state of the application. Now here we are considering the counter app, so our state is very simple, it’s just an integer which represents the counter’s current value.

Flutter Bloc Transitions:

The changes from one state to another state is called a Transition. A Transition consists of the current states, the event, and the next state.

Flutter Bloc Streams:

A stream is an asynchronous data sequence. To understand the stream consider this example. If you are new to Streams just think of a pipe with water flowing through it. Here the pipe is the Stream and the water is the asynchronous data that flows through this stream.

 

Flutter Blocs:

A Bloc stands for Business Logic Component component which converts a Stream of incoming Events into a Stream of outgoing States. to get the response to the user inputs.

 

Flutter Bloc Delegate:

The Beauty of Bloc is that we can have access to all Transitions in one place. If you want to be able to do something in response to all Transitions  at one place you can simply create your own BlocDelegate.

Flutter Bloc Widgets:

The BlocBuilder is a Flutter widget needs a Bloc and a builder function. BlocBuilder used to handle building the widget in response to new states. BlocBuilder is very similar to StreamBuilder but, it has a simple API to reduce the amount of boilerplate code needed. The builder function will be called many times and it will be a pure function that returns a widget in response to the state.

See BlocListener if you want to “do” anything in response to state changes such as navigation, showing a dialog, etc. use the below

 

BlocBuilder(
bloc: BlocA(),
builder: (context, state) {
// return widget here based on BlocA's state
}
)

The BlocProvider is a Flutter widget that provides a bloc to its children via BlocProvider.of<T>(context). It will be used as a DI widget so that a single instance of a bloc will be provided to multiple widgets within a subtree. As shown in below example code.

BlocProvider(
bloc: BlocA(),
child: ChildA(),
);

Flutter BlocProviderTree:

 It is a Flutter widget that merges multiple BlocProvider widgets into one.BlocProviderTree improves the readability and eliminates the need to nest multiple BlocProviders. By using BlocProviderTree we can go from: Please see below two sets of codes to understand this.

From:

BlocProvider(
bloc: BlocA(),
child: BlocProvider(
bloc: BlocB(),
child: BlocProvider(
value: BlocC(),
child: ChildA(),
)
)
)

To:

BlocProviderTree(
blocProviders: [
BlocProvider(bloc: BlocA()),
BlocProvider(bloc: BlocB()),
BlocProvider(bloc: BlocC()),
],
child: ChildA(),
)

Flutter BlocListener :

It is a Flutter widget uses a Bloc and a BlocWidgetListener and invokes the listener method while getting the response to state changes in the bloc. It is used to perform the functionality that needs to occur once per state changes such as navigation, showing a SnackBar, showing a Dialog,  increasing or decreasing the value for the counter app, etc.

The listener called once for each state changes including initialState

Use the below code to implement Flutter Bloc Listener Functionality.

BlocListener(
bloc: _bloc,
listener: (context, state) {
if (state is Success) {
Navigator.of(context).pushNamed('/details');
}
},
child: BlocBuilder(
bloc: _bloc,
builder: (context, state) {
if (state is Initial) {
return Text('Press the Button');
}
if (state is Loading) {
return CircularProgressIndicator();
}
if (state is Success) {
return Text('Success');
}
if (state is Failure) {
return Text('Failure');
}
},
}
)

Flutter BlocListenerTree:

 It is a Flutter widget that merges multiple BlocListener widgets into one.BlocListenerTree improves the readability and it also eliminates the need to nest multiple BlocListeners. By using BlocListenerTree we can go from: as shown in below example code.

Moving from:
BlocListener(
bloc: BlocA(),
listener: (BuildContext context, BlocAState state) {},
child: BlocListener(
bloc: BlocB(),
listener: (BuildContext context, BlocBState state) {},
child: BlocListener(
bloc: BlocC(),
listener: (BuildContext context, BlocCState state) {},
child: ChildA(),
),
),
)
Moving To:

BlocListenerTree(
blocListeners: [
BlocListener(
bloc: BlocA(),
listener: (BuildContext context, BlocAState state) {},
),
BlocListener(
bloc: BlocB(),
listener: (BuildContext context, BlocBState state) {},
),
BlocListener(
bloc: BlocC(),
listener: (BuildContext context, BlocCState state) {},
),
],
child: ChildA(),
)

Creating Flutter Bloc Functionality:

Add the dependency package:

adding the dependency package to pubspec.yaml file. use the below code to add dependency package. After adding the dependency package run the get package method to import all the required files to the app. 

dependencies:
flutter_bloc: ^0.13.0

Install the package:

You can install the package from the command line using the below code with Flutter as shown.

$ flutter packages get

Importing the Package:

After, Adding the dependency package to the pubspec.yaml file, you can now import the package into the dart code by using the below code. without adding the dependency package to the pubspec.yaml file if you import it will show package not found an error.

import 'package:flutter_bloc/flutter_bloc.dart';

counterbloc.dart:

Example code to create a Counter Bloc app using BlockBuilder and CounterPage as shown in below example code.

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 currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}

counter_page.dart

 

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

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

Complete Code for Flutter Bloc:

Copy and Paste Below Code into your main.dart file.

main.dart

import 'dart:async';

import 'package:flutter/material.dart';

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

class SimpleBlocDelegate extends BlocDelegate {
@override
void onEvent(Bloc bloc, Object event) {
super.onEvent(bloc, event);
print(event);
}

@override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print(transition);
}

@override
void onError(Bloc bloc, Object error, StackTrace stacktrace) {
super.onError(bloc, error, stacktrace);
print(error);
}
}

void main() {
BlocSupervisor().delegate = SimpleBlocDelegate();
runApp(App());
}

class App extends StatefulWidget {
@override
State createState() => _AppState();
}

class _AppState extends State {
final CounterBloc _counterBloc = CounterBloc();
final ThemeBloc _themeBloc = ThemeBloc();

@override
Widget build(BuildContext context) {
return BlocProviderTree(
blocProviders: [
BlocProvider(bloc: _counterBloc),
BlocProvider(bloc: _themeBloc)
],
child: BlocBuilder(
bloc: _themeBloc,
builder: (_, ThemeData theme) {
return MaterialApp(
title: 'Flutter Demo',
home: CounterPage(),
theme: theme,
);
},
),
);
}

@override
void dispose() {
_counterBloc.dispose();
_themeBloc.dispose();
super.dispose();
}
}

class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc _counterBloc = BlocProvider.of(context);
final ThemeBloc _themeBloc = BlocProvider.of(context);

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

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 currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}

enum ThemeEvent { toggle }

class ThemeBloc extends Bloc {
@override
ThemeData get initialState => ThemeData.light();

@override
Stream mapEventToState(ThemeEvent event) async* {
switch (event) {
case ThemeEvent.toggle:
yield currentState == ThemeData.dark()
? ThemeData.light()
: ThemeData.dark();
break;
}
}
}

Congratulations You Have Learned CreatingFlutter Bloc Functionality….!!!

If It Is Helpful Share It With Your Friends….!!!

Leave a Reply

Categories