Skip to content

Flutter BLoC Tutorial: State Management using BLoC Pattern

Document

What is BLoC?

BLoC is the abbreviation of the word Business Logic Components It aims to isolate the business logic of an application from its User Interface, making the application’s code simpler, flexible, adaptable, and capable of being tested.

  • Developed by: Felix Angelov
  • Sponsors: Very Good Ventures, Stream, Miquido
  • Version: flutter_bloc: ^8.0.1(at the time of writing article)

Pros & Cons of BLoC Design Pattern

Before we move on to the Flutter Block tutorial Let’s look at some of the advantages and disadvantages of the block design pattern.

Pros of Using BLoC

Great documentation of various scenarios.

Separates the business logic from the UI which makes the code understandable.

It makes the product more tastier.

It is simple to track the states that an app has gone through.

Cons of Using BLoC

The learning curve is steep.

Not recommended for applications with simple requirements.

A little more boilerplate code, but it is easily handled with extensions.

Flutter BLoC Tutorial Goal

We will create a basic application to demonstrate how the BLoC utilizes streams to manage state and create a few tests for the block.

We will create a basic application to demonstrate how the BLoC utilizes streams to manage state and create a few tests for the block.

Initial Setup

Make sure to install the bloc extension in your editor; it will help create all boilerplate code and files required for the project(right-click on the lib folder, and it will give you the option for generation bloc for your project).

Make sure to match your pubspec.yaml file with mine to avoid any issues.

Want to have easy and hustle-free Flutter application development?

Neo Infoway is waiting for you! Contact us today to hire a Flutter developer to meet the requirements of your project with outstanding problem-solving abilities.

Understanding BLoC Concepts: Events and States

To understand the way that blocks work We must know what constitutes states and events.

events: These are the inputs that an app can receive (like pressing a button to load images, inputs for text, or any other inputs from users that our application might wish to get).

State: State is the state of the application, and can be altered according to an event that is received.

Bloc manages these states and events, i.e., it is able to take an entire flow of Events and convert them into streams of States as output.

Creating an Event

                    
                        @immutable
                        abstract class AppBlocEvent {
                         const AppBlocEvent();
                        }
                         
                        @immutable
                        class ChangeTextEvent extends AppBlocEvent {
                         const ChangeTextEvent();}
                        
                    
                

Moving forward to the Flutter Block Tutorial. We have made an event called ChangeTextEvent which will be fired whenever the button is pressed.

We use an abstraction of the AppBlocEvent Class since Bloc is expecting a single occasion to add to its stream. However, since there could be multiple events running in an app, we design an abstract class and then extend it when we need to add any new event and pass on multiple events to Bloc.

Creating a State

Event and State Management using BLoC Pattern

                    
                        class AppBlocBloc extends Bloc {
                            final List textList = [
                              'Initial Text',
                              'Changed Text',
                              'Changed Again',
                            ];
                            AppBlocBloc() : super(const AppState.empty()) {
                              on((event, emit) {
                                try {
                                  int newIndex = state.index + 1;
                                  if(newIndex >= textList.length) {
                                    newIndex = 0;
                                  }
                                  emit(
                                    AppState(
                                      index: newIndex,
                                      text: textList[newIndex],
                                    ),
                                  );
                                } on Exception catch (e) {
                                  // ignore: avoid_print
                                  print(e);
                                }
                              });
                            }
                           }
                           
                

Explanation

This is the section which contains the software.

When a it is added into the stream with clicking a button. It gets the event, i.e. the information you wish to share with the triggering events, you are able to access via this(like event.any_info which requires you to alter your event’s class to reflect this) emit is used to generate an event state specific to that event.

When a it is added into the stream with clicking a button. It gets the event, i.e. the information you wish to share with the triggering events, you are able to access via this(like event.any_info which requires you to alter your event’s class to reflect this) emit is used to generate an event state specific to that event.

state.index allows you to see the state that is currently in the application’s state.index. It lets you see the current state of the.

emit(AppState (…)): emit (…) is used to generate new states and triggers the rebuilding part of the built() build() function.

Connect the pieces.

At present the events, states, blocks and our application’s UI are not linked to each other in any manner. Let’s get them all connected.

Providing our BLoC

                    
                        import 'package:flutter/material.dart';
                        import 'package:flutter_bloc/flutter_bloc.dart';
                        import 'package:text_change/text_controller.dart';
                        
                        import 'bloc/app_bloc_bloc.dart';
                        import 'bloc/app_bloc_state.dart';
                        
                        class App extends StatelessWidget {
                          const App({Key? key}) : super(key: key);
                        
                          @override
                          Widget build(BuildContext context) {
                            return MaterialApp(
                              title: 'Flutter Demo',
                              theme: ThemeData(
                                primarySwatch: Colors.blue,
                              ),
                              home: BlocProvider(
                                create: (context) => AppBlocBloc(),
                                child: Scaffold(
                                  appBar: AppBar(
                                    title: const Text('Text Change'),
                                  ),
                                  body: BlocConsumer(
                                    listener: (context, state) {},
                                    builder: (context, state) {
                                      return Text Controller(
                                        text: state.text,
                                      );
                                    },
                                  ),
                                ),
                              ),
                            );
                          }
                        }
                        
                        
                    
                

Explanation: App.dart

BlocProvider (…): We use it to display an instance of our block by placing it under the base of the application to make it accessible to all users.

Create: It creates the version of our AppBloBloc.

BlocConsumer (…): It is the place in which everything takes place.

builder: ‘s responsible for creating it’s UI which is then rebuilt with every state changes. blocConsumer also includes listenWhen and buildWhen that, like the name suggests, can be customized to respond to specific state changes.

Triggering the Event and States

                    
                        class TextChangeController extends StatelessWidget {
                            final String text;
                            
                            const TextChangeController({Key? key, required this.text}) : super(key: key);
                            
                            @override 
                            Widget build (BuildContext context) { 
                                  return Column
                                     children:  [ 
                                        TextChange(
                                           text: text, 
                                        ), // TextChange 
                                       ElevatedButton( 
                                           onPressed: () =>
                                                context.read().add(const ChangeTextEvent()), 
                                          child: const Text('Change Text'), 
                                     ), // ElevatedButton 
                                  ), // [ ] 
                               ); // Column
                              )
                            )
                            
                

In this case, we’ve included the event called ChangetTextEventonto an event’s stream which triggers a state change that triggers the rebuilding of BlocConsumer’s builder() inside BlocConsumer and then the modified text will show to the right of the screen.

You’ve got it You’re done! With a separate UI with business logic you can alter the UI code, and then connect the Bloc into. It will operate in the same way.

Testing the BLoC Design Pattern

For testing the bloc, you require two packages:

  • bloc_test
  • flutter_test

Simply go to the folder for testing, make the app_bloc_test.dart file and begin writing the test.Inside we’ll test two requirements:

Initial state of application i.e AppState.empty().State changes when the button is pressed.

                            
                                void main() {
                                    blocTest(
                                      'Initial State',
                                      build: () => AppBlocBloc(),
                                      verify: (appState) =>
                                          expect(appState.state, const AppState.empty(), reason: 'Initial State'),
                                    );
                                    blocTest(
                                      'emits [MyState] when MyEvent is added.',
                                      build: () => AppBlocBloc(),
                                      act: (bloc) => bloc.add(const ChangeTextEvent()),
                                      expect: () => const [
                                        AppState(
                                          index: 1,
                                          text: 'Changed Text',
                                        ),
                                      ],
                                    );
                                   }
                                   
                        

Explanation

  • blocTest is a part of the package bloc_test.
  • Build() It returns an example of AppBlocBloc().
  • Expect and verify in the sense that they are in line with the state expect(actual matcher, actual).
  • Act: To insert an event to the stream

Github Repository: Flutter BLoC Simple Example

Feel free to copy your repository the flutter-bloc demo and begin to play around using the code.

Frequently Asked Questions (FAQs)

BLoC (Business Logic Component) is a design pattern used for managing state in Flutter applications. It helps separate business logic from UI components, making code more modular, testable, and maintainable. BLoC pattern is widely used in Flutter for its simplicity and scalability in managing complex stateful applications.
In the BLoC pattern, business logic is encapsulated within separate BLoC classes, which act as intermediaries between UI components and data sources. BLoC classes receive events from the UI, process them, update the application state, and emit new states to notify UI components of changes.
The key components of the BLoC pattern in Flutter include:
  • Events: Represent user actions or system events that trigger state changes.
  • BLoC: Business Logic Component that contains the application’s business logic and manages state.
  • States: Represent different states of the application, which are emitted by the BLoC in response to events.
  • UI Components: Widgets that display the application’s state and respond to user interactions.
To implement the BLoC pattern in Flutter, you typically define BLoC classes to manage state, create events and states to represent user actions and application states, use StreamControllers or Cubits to handle state changes, and integrate BLoCs with UI components using StreamBuilder or BlocBuilder widgets.
The benefits of using the BLoC pattern in Flutter include separation of concerns, improved code organization and maintainability, reusability of business logic across multiple UI components, testability with unit tests and widget tests, and scalability for managing complex stateful applications.
While Provider and Riverpod are alternative state management solutions in Flutter, the BLoC pattern offers a more structured and formal approach to managing state, particularly for large-scale applications with complex state requirements. BLoC provides better separation of concerns and facilitates better code organization compared to other solutions.
Common use cases for using the BLoC pattern in Flutter applications include managing form state, handling user authentication and authorization, fetching and caching data from APIs, implementing complex user interfaces with dynamic behavior, and coordinating interactions between multiple screens or components.
While the BLoC pattern offers many advantages, it can introduce some complexity, especially for beginners. Setting up BLoC architecture and managing streams can require a learning curve. Additionally, boilerplate code and increased file count may be a concern for smaller projects.
Developers can find tutorials and resources for learning about the BLoC pattern in Flutter on official Flutter documentation, community forums like Stack Overflow and GitHub, developer blogs and tutorials, online courses and certifications, and Flutter conferences and meetups. Additionally, exploring sample projects and GitHub repositories can provide hands-on experience and insights into best practices.
Best practices for using the BLoC pattern effectively in Flutter applications include keeping BLoC classes focused on specific domains or features, minimizing dependencies between BLoC classes, using dependency injection for managing BLoC instances, naming conventions for events and states, and writing comprehensive unit tests to ensure correctness and reliability.
0 +
Projects
0 +
Clients
0 +
Years of Experience
0 +
Startups

WANT TO START A PROJECT?