Answer
Overview
Equatable is a Dart package that overrides
text
==text
hashCodeThe 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
yamldependencies: equatable: ^2.0.5
dartimport '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
dartclass 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
dartclass 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
| Benefit | Description |
|---|---|
| No duplicate rebuilds | Same state emitted twice → no rebuild |
| Cleaner code | No manual text text |
| Consistent behavior | Predictable equality across all states |
| Testing | Easier to assert state equality |
Rule: Always extend
for BLoC states and events. Add all fields totextEquatable. This prevents subtle bugs where the same data causes unexpected UI rebuilds.textprops