Flutter BLoC pattern

Posted by

This repository is for understanding the core concepts of BLoC pattern in flutter.

BLoC – Udemy

This tutorial video is also available on youtube. If you want, then instead of buying the Udemy tutorial, you can follow the youtube tutorial. The full documentation on BLoC package is available in bloclibrary.dev.

Bloc was designed with three core values in mind:

1. Simple: Easy to understand & can be used by developers with
               varying skill levels.

2. Powerful: Help make amazing, complex applications by composing
               them of smaller components.

3. Testable: Easily test every aspect of an application so that we
               can iterate with confidence.

Overall, Bloc attempts to make state changes predictable by regulating when a state change can occur and enforcing a single way to change state throughout an entire application.

BLoC Core Concept

1. Stream:

It is the foundation of BLoC. The stream is a river , which transport some data, on a boat, from the sender to the receiver. The transported data is asynchronous.

2. Cubit:

A cubit is the minimal version of a BLoC. BLoC actually extends cubit.

3. Bloc:

BLoC is the big brain of the project. Where as cubit is used to optimize the functionality of the project.

**Note: Start with a cubit. If you see the necessity, then modify your cubit into BLoC.

Flutter BLoC Concept

Some vital Flutter concepts to get going (widget, widget tree). 1. BlocProvider + CounterApp implementation. 2. BlocBuilder + CounterApp implementation. 3. BlocListener + CounterApp implementation. 4. BlocConsumer + CounterApp implementation. 5. RepositoryProvider 6. MultiBlocListener, MultiBlocProvider, MultiRepositoryProvider

RepositoryProvider is a class. Which has the main function which makes flutter communicate with outer data layer i.e. internet, APIs, databases, etc.

BlocBuilder & BlocListener can be combined together to from BlocConsumer

BLoC Architecture

BLoC follows a specific folder pattern. The “Business logic” layer is separated from the “Presentation” layer and from the “Data” layer.

 1. First design the models. A model is a blueprint of the data an application will work with.
 2. Next the data providers. The data providers’ responsibility is to provide raw data to it’s successor, which is the repositories. It is actually an API for our own application.


 3. The repository is mainly a wrapper around one, or more data providers. Repositories are also classes, which contains dependencies of the respective data providers.

BLoC Testing

A test is defined by how we programmatically tell flutter to double check the output given by a feature is equal to the expected response we planned on receiving. Packages needed for testing a BLoC:

dependencies:
  flutter:
    sdk: flutter
  # Helpful for testing equal instances
  equatable: ^2.0.3
  # For testing
  bloc_test: ^9.0.3

In Dart language, TWO INSTANCES of the same exact class are not equal even though they are basically identical. This is because, these two instances are stored in different part of the memory, and dart compares their location in memories instead of their value.

Hence, we need equatable package to solve this problem. Equatable package simply @overrides the equal operator.

    CounterState stateA = CounterState(counterValue:0);
    CounterState stateB = CounterState(counterValue:0);

                statA != stateB

BLoC Access

  BlocProvider() vs BlocProvider.value()

  - BlocProvider() -> Necessary when transferring state to the HomeScreen()
  - BlocProvider.value() -> Necessary for transferring state to the SecondScreen() & also
                            to the ThirdScreen()
  • Local Access: Providing an instance of bloc/cubit to a SINGLE SCREEN is called local access. Example: wrapping the HomeScreen() with BlocProvider().
  • Route Access: Providing an instance of bloc/cubit to MULTIPLE SCREEN is called route access. Example: wrapping the SecondScreen() & ThirdScreen() with BlocProvider.value().
  • Global Access: Providing am instance of bloc/cubit to EVERY SCREEN to your application. Example: wrapping the MaterialApp() with BlocProvider() or MultiBlocProvider().

Three types of routing option in Flutter:

  1. Anonymous routing: Navigation WITHOUT a RouteName.
  2. Named routing: Navigation WITH a RouteName. Recommended for small & medium size project.
  3. Generated routing: Separating the route information into a separate file. Recommended for large size project.

BLoC Communication

A Bloc/Cubit can communicate with each other using StreamSubscription or BlocListener. Both methods are equally good with their specific PROS/CONS

PROS of StreamSubscription:

  • organized, structured, easy to read & maintain.
  • will help us practice stream skills.

CONS of StreamSubscription:

  • it may get cluttered really fast on huge apps
  • not closing streamSubscription => huge memory leaks


———————————————– xxxx ———————————————–

BlocListener is a widget, hence it should be inside the widget tree. This just notifies the bloc/cubit. It tells the bloc/cubit what to do, not how to do it.

PROS of BlocListener:

  • It takes care internally of all STREAMSubscriptions
  • No need to take care of stream/memory leaks anymore

CONS of BlocListener:

  • The UI may get cluttered & hard to read with multiple BlocListener

BuildContext In-Depth

The BuildContext is a tool which helps handle the location of the widget inside the widget tree. That means every widget is build within a BuildContext.

One of the most common ERROR of bloc/cubit is:

"BlocProvider.of() fails to find
a context containing a specific
bloc/cubit"

A BuildContext of a widget keeps tract only of their direct parent and nothing else. Hence, the relationship between the BuildContext is a bottom-up relationship.

Fow of the BLoC contexts introduced in bloc: ^6.1.0:

  1. context.watch()
  2. context.select()
  3. context.read()

Hydrated BLoC

Hydrated BLoC is used mainly for storing the state of a bloc/cubit. hydrated_bloc exports a Storage interface which means it can work with any storage provider. Out of the box, it comes with its own implementation: HydratedStorage.

HydratedStorage is built on top of HIVE for a platform-agnostic, performant storage layer. See the complete example for more details.

Packages necessary for using hydrated bloc are:

# For storing state into the device
hydrated_bloc: ^8.1.0
# For retrieving path for data storage
path_provider: ^2.0.11

BLoC Debugging

App Screenshots

</table

Folder No.Name of the AppLogic FilesScreenshots
3Counter Using Cubitcounter_cubit.dart
counter_state.dart
5BLoC Testingcounter_cubit.dart
counter_state.dart
6BLoC Access & Routecounter_cubit.dart
counter_state.dart
7.0BLoC Communication
using
StreamSubscription
counter_cubit.dart
counter_state.dart
internet_cubit.dart
internet_state.dart
7.1BLoC Communication
using
BlocListener
counter_cubit.dart
counter_state.dart
internet_cubit.dart
internet_state.dart
10State Not Updatingsettings_cubit.dart
settings_state.dart
11Hydrated BLoCcounter_cubit.dart
counter_state.dart
12Debuggingcounter_state.dart
internet_state.dart
settings_state.dart

GitHub

View Github

#Flutter #bloc_concept #BLoc_Pattern

Leave a Reply

Your email address will not be published. Required fields are marked *