What is BLoC?

BLoC stands for Business Logic Components; it aims to separate the application’s business logic from User Interface, making the application code more unambiguous, scalable, and testable.

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

Pros & Cons of BLoC Design Pattern

Before moving forward in the flutter bloc tutorial, let’s examine some of the pros and cons of the bloc design pattern.

Pros of Using BLoC

  • Excellent documentation about different scenarios.
  • Separates business logic from the UI hence making the code easy to understand.
  • Makes the product more testable.
  • Easy to keep track of states an app went through.

Cons of Using BLoC

  • The learning curve is a bit steep.
  • Not recommended for simple applications
  • More boilerplate code, but that can be taken care of by extensions.

Flutter BLoC Tutorial Goal

We will build a relatively simple application to demonstrate how the BLoC uses streams to manage states and write some tests for the bloc.

We will build a text change application; press text will get changed and displayed with each button. You can refer below GIF for the same.

Flutter BLoC Tutorial Goal

Initial Setup

1. 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).

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

Folder Structure of Flutter BLoC Simple Example

Want to have easy and hustle-free Flutter application development?
Bacancy is here for you! Contact us to hire Flutter developer to fulfill your project requirements efficiently with commendable problem-solving skills.

Understanding BLoC Concepts: Events and States

Let the game begin.

To understand how bloc works, we need to know what are events and states.

  • Events: events are an application’s inputs (like button_press to load images, text inputs, or any other user input that our app may hope to receive).
  • States: States are simply the application’s state, which can be changed in response to the event received.

Bloc manages these events and states, i.e., it takes a stream of Events and transforms them into a stream of States as output.

Creating an Event

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

Moving forward in the flutter bloc tutorial. Here we have created a ChangeTextEvent, which will be fired when a button is clicked.

We have an abstract AppBlocEvent class because Bloc expects a single event to be added to the stream. Still, as there can be multiple events in an app, we create an abstract class and extend it whenever we want to create any new event for handling and passing multiple events to the bloc.

Creating a State

Copy Text
@immutable
class AppState extends Equatable {
 final int index;
 final String text;
 
 const AppState.empty()
     : index = 0,
       text = 'Initial Text';
 
 const AppState({
   required this.index,
   required this.text,
 });
 
 @override
 List<Object> get props => [index, text];
}

Similarly, we can create different states here in this app. We do not have many states. Therefore, we have created a single state to manage the app; however, we can create multiple states similar to events by creating an abstract appstate and extending it to our custom states.

  • AppState.empty is simply the initial state with the application loads initially.
  • Equatable (get props) here is used to compare to states. If they are equal, it will be used in testing the bloc.

Event and State Management using BLoC Pattern

Copy Text
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 part that contains the business logic of our application.

  • on is executed when ChangeTextEvent is added to the stream via a button click , it receives the event, i.e., any information that you want to pass along with triggering event you can access it using this(like event.any_info – you have to change your event class accordingly), emit which is used to emit a state for that particular event.
  • state.index lets you access the current state of the application.
  • emit(AppState(…)): emit(…) is used to output new states and causes the rebuild of the build() function.

Put the pieces together.
Till now, events, states, bloc, and our application’s UI are not connected in any way. Let’s start piecing them together.

Providing our BLoC

Copy Text
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 TextController(
                text: state.text,
              );
            },
          ),
        ),
      ),
    );
  }
}

Explanation: App.dart
BlocProvider(…): we use it to provide an instance of our bloc by placing it just below the root of the application so that it is accessible throughout it.

  • create: it creates the instance of our AppBloBloc.

BlocConsumer(…): It is the area where everything happens.

  • It has a property called listener, which listens for state changes and can react in a particular way to a specific state along with state change.
  • builder: it is responsible for building the UI and is rebuilt for each state change. blocConsumer also contains listenWhen and buildWhen, which as the name states, can be tailored to react to specified states.

Triggering the Event and States

Copy Text
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
  )
)

Here we have added ChangetTextEvent onto the event stream, thereby triggering state change which causes the rebuild of the builder() in BlocConsumer, and changed text is displayed up on the screen.

There you have it!! With separated UI and business logic, you can change the UI code and just plug the Bloc in. It will work the same.

Are you willing to build a demo application and learn to work with freezed in your flutter app?

Flutter Freezed Example

Testing the BLoC Design Pattern

For testing the bloc, you require two packages:

  • bloc_test
  • flutter_test

Simply inside the test folder, create the app_bloc_test.dart file and start writing test.
Inside we are going to test two conditions:

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

There you go!!😊😊

Copy Text
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 coming from the bloc_test package.
  • build(): it returns an instance of AppBlocBloc().
  • verify and expect as the name suggests they match the state expect(actual, matcher, reason).
  • act: To add an event onto the event stream.

Github Repository: Flutter BLoC Simple Example

Feel free to clone the repository: flutter-bloc-demo and start experimenting with the code.

Conclusion

The Flutter BLoC tutorial was helpful for you to get started with state management using the BLoC pattern. We will be back with another Flutter tutorial; till then, visit the Flutter tutorials page and learn more about Flutter concepts. Let us know if you want any specific topics to be covered. Write us back your suggestions and feedback. Happy Coding!

Outsource Team of Dedicated Flutter Developers

  • Profound Technical Knowledge
  • Integrity & Transparency
  • Result-Driven Approach

BOOK A 30 MIN CALL

Get In Touch

solut[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.