Work with BLoC in Flutter

Hey Folks, welcome back to another article….

Today we are going to learn about “how to architect your Flutter projects”.“how to architect your Flutter projects”. So that you can maintain, scale, and test your Flutter projects easily, and to do that we need state Management approaches. You might have heard about different state managements such as BLoc Pattern, Riverpod, Mobx, Redux, Getx, Provider. We gonna learn about BLoC in a flutter. First of all, let’s talk about what is BLoC? It stands for Business Logic Components and it is one of the state management in a flutter recommended by Google developers and What it does? It separates our business logic from the presentation layer(UI part).

You may think that why should we use this as we can do these things simply using StateFulWidget? so Let’s see what happens while using STF(StateFulWidget) It will rebuild your widget tree every time we call setState or any of the widgets changes their state. On the other hand, using bloc we can overcome that as it only rebuilds widget whose value gets updated instead of the whole tree.

Here we see How it works

So, to manage these things we need to structure our project: we need a bloc folder for every screen which contains three classes Bloc, State, and Events.

Now moving to implement BLoC pattern.

Create a fresh flutter project. You know how to create a flutter project right!

we required some packages for the bloc to import, so first add those in pubspec.yaml

flutter_bloc: ^7.0.1
bloc: ^7.0.0

Now run  flutter pub get

Create files as following:

 

 

 

 

 

 

1. write main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

/// Custom [BlocObserver] which observes all bloc and cubit instances.
class SimpleBlocObserver extends BlocObserver {
  @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(BlocBase bloc, Object error, StackTrace stackTrace) {
    print(error);
    super.onError(bloc, error, stackTrace);
  }
}

void main() {
  Bloc.observer = SimpleBlocObserver();
  runApp(App());
}

/// A [StatelessWidget] which uses: bloc and flutter_bloc
/// to manage the state of a counter.
class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => ThemeCubit(),
      child: BlocBuilder<ThemeCubit, ThemeData>(
        builder: (_, theme) {
          return MaterialApp(
            theme: theme,
            home: BlocProvider(
              create: (_) => CounterBloc(),
              child: CounterPage(),
            ),
          );
        },
      ),
    );
  }
}

/// A simple [Cubit] which manages the [ThemeData] as its state.
class ThemeCubit extends Cubit<ThemeData> {
  ThemeCubit() : super(_lightTheme);

  static final _lightTheme = ThemeData(
    floatingActionButtonTheme: const FloatingActionButtonThemeData(
      foregroundColor: Colors.white,
    ),
    brightness: Brightness.light,
  );

  static final _darkTheme = ThemeData(
    floatingActionButtonTheme: const FloatingActionButtonThemeData(
      foregroundColor: Colors.black,
    ),
    brightness: Brightness.dark,
  );

  /// Toggles the current brightness between light and dark.
  void toggleTheme() {
    emit(state.brightness == Brightness.dark ? _lightTheme : _darkTheme);
  }
}

Here I used  BlocObserver observes all bloc instance and  Cubit<ThemeData> to set a dynamic theme of the application

2. Write Bloc for Counter

/// A simple [Bloc] which manages an `int` (counter value) as its state.
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0);
  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield state - 1;
        break;
      case CounterEvent.increment:
        yield state + 1;
        break;
      case CounterEvent.error:
        addError(Exception('unsupported event'));
    }
  }
}

We need to override  mapEventToState  to manage states on every occurrence of events and put your logic according to event.

3. Create events that are executed according to user interaction.

/// Event being processed by [CounterBloc].
enum CounterEvent {
  /// Notifies bloc to increment state.
  increment,

  /// Notifies bloc to decrement state.
  decrement,

  /// Notifies the bloc of an error
  error,
}

As we have few events for counter I used enum for simplicity.

4. Finally create the user interface.

/// A [StatelessWidget] which demonstrates
/// how to consume and interact with a [CounterBloc].
class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: BlocBuilder<CounterBloc, int>(
        builder: (_, count) {
          return Center(
            child: Text('$count', style: Theme.of(context).textTheme.headline1),
          );
        },
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: const Icon(Icons.add),
              onPressed: () =>
                  context.read<CounterBloc>().add(CounterEvent.increment),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: const Icon(Icons.remove),
              onPressed: () =>
                  context.read<CounterBloc>().add(CounterEvent.decrement),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: const Icon(Icons.brightness_6),
              onPressed: () => context.read<ThemeCubit>().toggleTheme(),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              backgroundColor: Colors.red,
              child: const Icon(Icons.error),
              onPressed: () =>
                  context.read<CounterBloc>().add(CounterEvent.error),
            ),
          ),
        ],
      ),
    );
  }
}

In this class we need  BlocBuilder  as a parent widget of a text widget that displays counter because it will update our widget according to changes in states or on event call.

To perform any operation we just need to call a specific event like

context.read<BlocName>().add(EventName)

‘context’ used in this is referring to the bloc provided in specific  BlocBuilder

So we have arrived at the end of this article. Thank You for holding on till the end. Hope you like this article and will help you to start working with BLoC. If you have any doubts or questions do comment.

See you in the Next Article.

Submit a Comment

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

Subscribe

Select Categories