Simplest Flutter Bloc Pattern

how to use bloc pattern in flutter with multiple state class#

Flutter is very powerfull framework as well as sdk for cross platform mobile app development.

There are multiple state management library in flutter but we are going to discuss about Bloc which is Business Logic Component .

There are two parts in Bloc one is Cubit and second is Bloc. Bloc works on Streams and States but Cubit is almost same like Bloc but without Streams.

Unlike other state management like in providers we have to write a little more boilerplate code for Bloc.

We will implement basic use of Bloc like fetching data and showing in list.

First we will create state class which will be abstract class like below with initial loading state.


part of 'service_search_bloc.dart';

@immutable
abstract class SearchState {}

class SearchLoading extends SearchState {}

later we can create multiple state class based on behaviour of our app

I am going to add two more one for after loading finished and one for api error, so my state will look like below.

search_state.dart

part of 'search_bloc.dart';

@immutable
abstract class SearchState {}

class SearchLoading extends SearchState {}

class SearchLoadingFinished extends SearchState {
  SearchLoadingFinished({required this.employeeList});
  final List<Employees> employeeList;
}

class SearchError extends SearchState {
  SearchError({required this.error});
  final String error;
}


Now lets create events for that particular search operation. There will be only one event like fetchingEmployee event. event is like action user is doing on your app.

below is the event class of our Bloc.

search_event.dart

part of 'search_bloc.dart';

@immutable
abstract class SearchEvent {}

class GetEmployeeList extends SearchEvent {}


if you are performing action on fetched list like delete, update, checked you can create those events class and extends SearchEvent

now lets create a SearchBloc class which will hold the logic for fetching and emitting state to view layer.

I am using Dio package to call the api under the hood, i have created a seperate Http service class which holds the logic for calling api’s based on methods

search_bloc.dart

part 'search_event.dart';
part 'search_state.dart';

class SearchBloc extends Bloc<SearchEvent, SearchState> {
  SearchBloc() : super(SearchLoading()) {
    
    on<GetEmployeeList>((event, emit) async {
      try {
      
        var response = await Http.instance.Get("api call url");

        if (response?.statusCode == 200) {

        Map<String, dynamic> data = response?.data;

        List<Employees> employeeList = List<Employees>.from(
            data["data"]["employees"].map((e) => Employees.fromJson(e)));

        emit(SearchLoadingFinished(employeeList: employeeList));

        }
      
      } catch (err) {

        if (err is DioError) {
          emit(SearchError(error: err.message));
        }
        

      }
    });
  }
}


I will use BlocBuilder to update the view you can also use BlocListener and BlocConsumer they holds some extra functionality.

I will create a seperate post for when to use either of above three to update the view.

I have created a stateless widget. Check the below code for our employees listing

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:project_name/bloc/search_bloc.dart';
import 'package:project_name/models/employees.dart';

class EmployeeSearchResult extends StatelessWidget {

@override
  Widget build(BuildContext context) {
    BlocProvider.of<SearchBloc>(context, listen: false)
        .add(GetEmployeeList());

    return Scaffold(
     
      body: BlocBuilder<SearchBloc, SearchState>(
          builder: (context, state) {

        if (state is SearchLoading) {

          return const Center(child: CircularProgressIndicator());

        } else if (state is SearchLoadingFinished) {

          return ListView.builder(
              itemCount: state.employeeList.length,
              itemBuilder: (context, index) {
                Employees emp = state.employeeList[index];
                return ListTile(title: Text("${emp.first_name} ${emp.last_name}"));
              });

        } else if (state is SearchError) {

          return Center(
            child: Text("${state.error}"),
          );

        }
        return Center(
          child: Text("Something Went Wrong"),
        );
      }),
    );
  }

}

I will also attach Employee model class so you dont get confused. that is just basic class with some properties.

employee.dart


class Employees {

 final String firstName;
 final String lastName;


 Employees(
     {
       required this.firstName,
       required this.lastName,
     });

 factory Employees.fromJson(Map json) {
   return Employees(
       firstName: json["first_name"],
       lastName: json["last_name"],
       );
 }
}


comments powered by Disqus