Question #238EasyState Management

What is equatable in bloc and use of it ?

#bloc

Answer

Overview

Equatable is a Dart package that overrides

text
==
and
text
hashCode
automatically based on the fields you specify — preventing unnecessary BLoC state rebuilds when the same state is emitted with the same values.


The Problem Without Equatable

dart
// Without Equatable
class LoginFailure extends LoginState {
  final String error;
  LoginFailure(this.error);
}

// Two instances with same error are NOT equal
LoginFailure('Invalid password') == LoginFailure('Invalid password'); // false!

// BLoC emits the same state again → BlocBuilder rebuilds unnecessarily!
emit(LoginFailure('Invalid password'));
emit(LoginFailure('Invalid password')); // Triggers rebuild even though same!

The Fix — Equatable

yaml
dependencies:
  equatable: ^2.0.5
dart
import 'package:equatable/equatable.dart';

class LoginFailure extends LoginState with EquatableMixin {
  final String error;
  LoginFailure(this.error);

  
  List<Object> get props => [error]; // ← Fields to compare
}

// Now these are equal!
LoginFailure('Invalid password') == LoginFailure('Invalid password'); // true!
// BLoC won't rebuild if same state is emitted twice

Using Equatable with BLoC Correctly

dart
// States
abstract class CounterState extends Equatable {}

class CounterValue extends CounterState {
  final int count;
  CounterValue(this.count);

  
  List<Object> get props => [count]; // Compare by count value
}

// Events
abstract class CounterEvent extends Equatable {
  
  List<Object> get props => [];
}

class IncrementCounter extends CounterEvent {}
class DecrementCounter extends CounterEvent {}

Equatable with Nullable and Complex Types

dart
class ProfileState extends Equatable {
  final String name;
  final String? avatar;           // Nullable
  final List<String> tags;       // List
  final Map<String, int> scores; // Map

  const ProfileState({
    required this.name,
    this.avatar,
    this.tags = const [],
    this.scores = const {},
  });

  
  List<Object?> get props => [name, avatar, tags, scores];
  // Use List<Object?> (nullable) when props can be null
}

Equatable vs Manual ==

dart
// ❌ Manual — tedious and error-prone
class UserState {
  final String name;
  final int age;

  
  bool operator ==(Object other) =>
    identical(this, other) ||
    other is UserState && name == other.name && age == other.age;

  
  int get hashCode => name.hashCode ^ age.hashCode;
}

// ✅ Equatable — clean and automatic
class UserState extends Equatable {
  final String name;
  final int age;

  
  List<Object> get props => [name, age];
}

stringify Property

dart
class CounterState extends Equatable {
  final int count;
  CounterState(this.count);

  
  List<Object> get props => [count];

  
  bool get stringify => true; // Enables CounterState(count: 5) in toString
}

Key Benefits in BLoC

BenefitDescription
No duplicate rebuildsSame state emitted twice → no rebuild
Cleaner codeNo manual
text
==
/
text
hashCode
Consistent behaviorPredictable equality across all states
TestingEasier to assert state equality

Rule: Always extend

text
Equatable
for BLoC states and events. Add all fields to
text
props
. This prevents subtle bugs where the same data causes unexpected UI rebuilds.