When using Bloc or Cubit, one of the most common performance pitfalls is unnecessary UI rebuilds. This happens because Bloc compares states using ==, and without proper equality implementation, two states that are logically identical may be considered different. Equatable solves this by providing a simple way to implement value‑based equality, ensuring your UI only rebuilds when the state actually changes.
The Problem: Reference Equality
By default, Dart uses reference equality for objects. Two separate instances with the same values are not considered equal:
When Bloc emits a new state, it compares the new state with the previous one using ==. If they are not equal, BlocBuilder and BlocListener trigger rebuilds. If you always create a new state instance (even when the data is the same), you'll cause unnecessary rebuilds, hurting performance.
What is Equatable?
Equatable is a Dart package that overrides == and hashCode for you. You simply extend Equatable and list the properties you want to be compared in a props getter. Then, two objects with the same values for those properties will be considered equal, regardless of whether they are the same instance.
Installation
Using Equatable in Bloc States
Using Equatable in Events
Events can also benefit from Equatable, especially when using event transformers or when you want to avoid duplicate event handling. It's not strictly required, but highly recommended.
How Equatable Prevents Unnecessary Rebuilds
Consider a scenario where you fetch data and then refresh it with the same data. Without Equatable, you'd create a new DataLoaded instance with the same list, but since it's a new object, Bloc thinks the state changed and rebuilds the UI. With Equatable, if the list contents are identical (and you've implemented equality correctly, e.g., using DeepEquality or Equatable for the list items), the states will be considered equal and no rebuild will occur.
Deep Equality with Collections
Equatable only compares the list references by default, not the contents. To compare the contents of collections, you need to either use immutable collections (like List.unmodifiable) or a deep equality helper. For most cases, if you're using immutable models with Equatable, the list contents will be compared if each item is also Equatable. Alternatively, you can use the ListEquality from the collection package.
Equatable with Freezed
freezed is a code generator that automatically creates immutable classes with copyWith, toString, and equality. It is often used as an alternative to manual Equatable implementations. Both work, but Freezed offers more features and less boilerplate for complex states.
Performance Considerations
- Use
Equatablefor all states and events – Prevents unnecessary rebuilds and makes debugging easier. - Keep the
propslist small – Only include fields that determine equality. Avoid including large objects that rarely change if they are not part of the logical state. - Be careful with collections – If you use mutable lists, the content may change without triggering a state change. Prefer immutable collections or ensure you emit a new state with a new list when content changes.
- Use
constconstructors when possible – This can help with performance and prevents accidental instance duplication.
Common Mistakes
- ❌ Forgetting to include
propsin subclasses – Causes equality to only compare the base class (which may have no fields), leading to incorrect equality. ✅ Always overridepropsin each concrete state class. - ❌ Including non‑final fields – If fields are mutable, equality can become inconsistent.
✅ Make all fields
final. - ❌ Omitting Equatable in events – If you use event transformers, you may miss events that are the same but different instances. ✅ Use Equatable for events as well.
- ❌ Assuming deep equality for lists – Equatable compares references for lists. If you modify the list contents without creating a new list, the state won't be considered changed. ✅ Use immutable collections or a custom deep equality.
Next Steps
Now that you understand how to implement proper equality, explore more about Bloc state management:
- Bloc States – Designing effective state classes.
- Cubit vs Bloc – Choosing the right approach.
- Bloc Testing – Writing tests for your blocs.
Conclusion
Equatable is a small but essential tool when working with Bloc or Cubit. By enabling value‑based equality, it prevents unnecessary UI rebuilds, reduces bugs, and makes your code more predictable. Always include it in your state and event classes for a smooth, performant Bloc experience.