BLoC is an acronym for business logic components. The Flutter BLoC package makes it easy to implement the bloc pattern recommended by Google (Google I/O 2018).
BLoC was created with three guiding principles in mind:
Simple: It’s simple to grasp and can be used by developers of all skill levels.
Powerful: Assist in the creation of remarkable, sophisticated applications by breaking them down into smaller components.
Testable: We can easily test every part of an application so we can confidently iterate.
There are four main layers of application in the BLoC pattern:
UI. The UI contains all of the application’s components that the user can see and interact with.
BLoC. The bloc is the layer between the data and the UI components. The bloc receives events from an external source and emits a state in response to the received event.
Repository. The repository is designed to be the single source of truth, it is responsible for organizing data from the data source(s) to be presented by the UI.
Data providers. The data providers are responsible for fetching the application data, they are characterized by network calls and database interactions.
Events: Events are input to a bloc, they’re usually added as a result of user activities like button pushes, or lifecycle events like page loads. You can model your event as anything, from a primitive data type, such as an integer, to any complex abstracted classes.
1
2
3
abstract class AuthEvent {}
class LoginEvent extends AuthEvent {}
class LogoutEvent extends AuthEvent {}
States: States are an output of a bloc, they represent the application state. The UI components listen to a state change and redraw a portion of themselves based on the current state. The state can also be modeled as anything from a primitive data type, such as an integer, to any complex abstracted classes.
1
2
3
abstract class AuthState {}
class UnAuthenticatedState extends AuthState {}
class AuthenticatedState extends AuthState {}
Bloc: A bloc uses an event to trigger a state change. Blocs are event receivers that turn incoming events into outgoing states.
1
2
3
class AuthBloc extends Bloc < AuthEvent, AuthState > {
AuthBloc(): super(UnAuthenticatedState());
}
To emit a new state, you need to handle triggered events. To do this, you will have to register an event handler using the on <Event>
API inside the constructor body as follows;
1 2 3 4 5 6 7 8 9
AuthBloc(): super(UnAuthenticatedState()) { on < LoginEvent > ((event, emit) { emit(AuthenticatedState()); }); on < LogoutEvent > ((event, emit) { emit(UnAuthenticatedState()); }); }
Note: Blocs should never emit new states directly, rather every state change should be emitted from the EventHandler.
Cubit: A cubit is a class that manages any kind of state by exposing functions that can be evoked to trigger state changes. Unlike a bloc, a cubit doesn’t use events to trigger state changes. Instead it exposes functions to trigger the state changes.
Cubit is created by extending the cubit generic class and defining the state like this;
1
2
3
class AuthCubit extends Cubit < AuthState > {
AuthCubit(): super(UnAuthenticatedState());
}
To emit a new state, you will have to call a function on the cubit object, each cubit can emit a new state using the emit method as follows;
1 2 3
AuthCubit(): super(UnAuthenticatedState()); void login() => emit(AuthenticatedState()); void logout() => emit(UnAuthenticatedState());
Note: Because the emit method is protected, it should only be used within a cubit.
The bloc widgets help to rebuild/notify the UI components in response to a state change. Since cubit is just a subclass of bloc, you can use cubit anywhere that a bloc is required when working with bloc widgets.
BlocProvider is a Flutter widget that uses BlocProvider.of <T>
to supply a bloc to its children. It’s a dependency injection (DI) widget that lets you deliver a single instance of a bloc to many widgets within a subtree. BlocProvider should be used to create new blocs that will be available to the remainder of the subtree in most cases. Because BlocProvider is in charge of generating the bloc in this situation, it will also be in charge of closing it.
You will almost always want to put the BlocProvider above the MaterialApp so that it will be available everywhere in your application.
1 2 3 4 5 6 7 8 9 10 11
BlocProvider( create: (BuildContext context) => AuthBloc(), child: MaterialApp( title: 'Flutter Bloc Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(), ), );
With this, you can use BlocProvider.of <AuthBloc>(context)
to get the instance of the auth bloc, and add an event like this;
BlocProvider.of<AuthBloc>(context).add(LoginEvent());
In other cases, you can use the BlocProvider to provide an existing bloc to a new aspect of the widget tree. When an existing bloc needs to be made available to a new route, this will be the most usually employed method.
1 2 3 4
BlocProvider.value( value: BlocProvider.of < AuthBloc > (context), child: ChildWidget(), ),
MultiBlocProvider is a Flutter widget that combines the functionality of numerous BlocProvider widgets into a single widget. MultiBlocProvider enhances readability by removing the requirement for numerous BlocProviders to be nestled.
Instead of having a nested BlocProvider that looks like this,
1 2 3 4 5 6 7 8 9 10
BlocProvider( create: (BuildContext context) => FirstBloc(), child: BlocProvider( create: (BuildContext context) => SecondBloc(), child: BlocProvider( create: (BuildContext context) => ThirdBloc(), child: ChildWidget(), ), ), ),
You can have all of these BlocProviders inside a single MultiBlocProvider like this;
1 2 3 4 5 6 7 8 9 10 11 12 13 14
MultiBlocProvider( providers: [ BlocProvider( create: (BuildContext context) => FirstBloc(), ), BlocProvider( create: (BuildContext context) => SecondBloc(), ), BlocProvider( create: (BuildContext context) => ThirdBloc(), ) ], child: ChildWidget(), ),
BlocBuilder is a Flutter widget that needs both a bloc and a builder function to work. BlocBuilder is in charge of constructing the widget in response to new states. BlocBuilder is similar to StreamBuilder, but it has a simpler API to cut down on boilerplate code.
BlocBuilder will automatically do a lookup using BlocProvider and the current BuildContext if you don’t include the bloc parameter.
1 2 3 4 5
BlocBuilder < AuthBloc, AuthState > ( builder: (context, state) { // return widget here based on AuthBloc's state }, ),
If you want to supply a bloc that is scoped to a single widget and isn’t accessible through a parent BlocProvider or the current BuildContext, this is when you should define the bloc parameter.
1 2 3 4 5 6
BlocBuilder < AuthBloc, AuthState > ( bloc: authBloc, builder: (context, state) { // return widget here based on AuthBloc's state }, ),
BlocListener is a Flutter widget that takes a BlocWidgetListener and an optional bloc and calls the listener when the state of the bloc changes. It should be used for functionality that only happens once per state change, such as navigation, displaying a SnackBar, displaying a dialog, and so on.
BlocListener will automatically conduct a lookup using BlocProvider and the current BuildContext if the bloc parameter is omitted.
1 2 3 4 5 6 7 8 9 10
BlocListener < AuthBloc, AuthState > ( listener: (context, state) { if (state is UnAuthenticatedState) { ScaffoldMessenger.of(context) .showSnackBar( SnackBar(content: Text("You are not logged in"))); } }, child: ChildWidget(), ),
To react to new states, the BlocConsumer exposes a builder and a listener. BlocConsumer works similarly to nested BlocListener and BlocBuilder, but with less boilerplate. BlocConsumer should only be used when both rebuilding the UI and performing additional reactions to state changes in the bloc are required.
BlocConsumer will automatically conduct a lookup using BlocProvider and the current BuildContext if the bloc parameter is omitted.
1 2 3 4 5 6 7 8
BlocConsumer < AuthBloc, AuthState > ( listener: (context, state) { // do stuff here based on AuthBloc's state }, builder: (context, state) { // return widget here based on AuthBloc's state }, ),
I hope you enjoyed learning about the basic concepts of Flutter BLoC in this article. In the next one we will build a Github repository search application using most of the concepts learned here. See you next time!
The official Bloc library documentation