Why we should use Equatable in Flutter Bloc?

Viewed 1585

I understand that Equatable helps to compare two instances of object without doing it manually. But where exactly I can use it in Flutter Bloc?

Here is the example of usage Equatable:
Where it could be useful?

abstract class TodosState extends Equatable {
  const TodosState();

  @override
  List<Object> get props => [];
}

class TodosLoadInProgress extends TodosState {}

class TodosLoadSuccess extends TodosState {
  final List<Todo> todos;

  const TodosLoadSuccess([this.todos = const []]);

  @override
  List<Object> get props => [todos];

  @override
  String toString() => 'TodosLoadSuccess { todos: $todos }';
}

class TodosLoadFailure extends TodosState {}
2 Answers

Object and data comparison is always hard to do when it comes to stream as we need to decide state updation based on it. we required Equatable as it overrides == and hashCode internally, which saves a lot of boilerplate code. In Bloc, we have to extend Equatable to States and Events classes to use this functionality.

 abstract class TodosState extends Equatable {}

So, that means TodosState will not make duplicate calls and will not going to rebuild the widget if the same state occurs.

Let's see props usage in Equatable and what makes it special

Define State without props:

  class TodosLoadSuccess extends TodosState {}

Define State with props:

props declared when we want State to be compared against the values which declared inside props List

class TodosLoadSuccess extends TodosState {
  final String name;
  final List<Todo> todos;
  const TodosLoadSuccess([this.name, this.todos = const []]);

   @override
   List<Object> get props => [name, todos];

 }

If we remove the name from the list and keep a list like [this.todos], then State will only consider the todos field, avoiding the name field. That is why we used props for handling State changes.

Bloc Stream Usage:

As we extending State with Equatable that makes a comparison of old state data with new state data. For example, let's look at the below example here TodosState will build a widget only once, which will avoid the second call as it is duplicated.

@override
Stream<TodosState> mapEventToState(MyEvent event) async* {
  final List<Todo> todos = [Todo(), Todo()];
  yield TodosLoadSuccess(todos);
  yield TodosLoadSuccess(todos); // This will be avoided
}

Detail Blog: https://medium.com/flutterworld/flutter-equatable-its-use-inside-bloc-7d14f3b5479b

I think it is useful for comparing what state is in BlocBuilder.
Below code is a good example of using Equatable.

if(state is [Some State])
@override
Widget build(BuildContext context) {
  return BlocBuilder<SongsSearchBloc, SongsSearchState>
    bloc: BlocProvider.of(context),
    builder: (BuildContext context, SongsSearchState state) {
      if (state is SearchStateLoading) {
        return  CircularProgressIndicator();
      }
      if (state is SearchStateError) {
        return Text(state.error);
      }
      if (state is SearchStateSuccess) {
        return state.songs.isEmpty
            ? Text(S.EMPTY_LIST.tr())
            : Expanded(
                child: _SongsSearchResults(
                  songsList: state.songs,
                ),
              );
      } else {
        return  Text(S.ENTER_SONG_TITLE.tr());
      }
    },
  );
}
Related