DEV Community

Cover image for Chapter 2: Mastering Equality with the Equatable Package in Flutter Bloc
Md. Al-Amin
Md. Al-Amin

Posted on

Chapter 2: Mastering Equality with the Equatable Package in Flutter Bloc

Welcome back to Chapter 2 of our Flutter Bloc State Management course! If you’ve been following along, we’ve set the foundation with an overview of Flutter Bloc, its significance, and why big companies trust it for state management. Today, we’re taking a closer look at an essential tool in Flutter Bloc’s toolkit: the Equatable package.

In this post, we’ll talk about comparing objects in Dart, why object equality is so important in state management, and how Equatable saves us from writing redundant, boilerplate code. By the end of this chapter, you'll have a solid understanding of object equality and know exactly when and how to use Equatable in your Flutter Bloc projects.

Why Equality Matters in State Management

Imagine you’re designing a to-do app. Each time you add or remove a to-do item, the state of your app changes. If the state isn’t accurately updated or recognized, your app might behave unpredictably. This is where checking if two objects are equal or not becomes essential. In Dart, you might expect the equality operator (==) to check for equality between two instances, but by default, it only checks if they’re the same instance in memory, not if they contain the same data.

Equality in Dart: The Basics

Dart provides two primary ways to check if objects are equal:

  • Equality Operator (==): Checks if two objects reference the same instance in memory.
  • Hash Code: Helps compare objects based on their properties.

Example: The Equality Operator and Hash Code in Action

Let’s say we have a User class with id and name properties. Here’s how we might check for equality without using Equatable.

class User {
  final int id;
  final String name;

  User({required this.id, required this.name});
}

void main() {
  var user1 = User(id: 1, name: 'Alice');
  var user2 = User(id: 1, name: 'Alice');

  print(user1 == user2); // false - even though they have the same data
}
Enter fullscreen mode Exit fullscreen mode

The result here is false because, by default, Dart checks if user1 and user2 are the same instance in memory, which they’re not.

Implementing Equality Manually

One solution is to override the == operator and hashCode in the User class manually. Here’s how that would look:

class User {
  final int id;
  final String name;

  User({required this.id, required this.name});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is User && runtimeType == other.runtimeType && id == other.id && name == other.name;

  @override
  int get hashCode => id.hashCode ^ name.hashCode;
}
Enter fullscreen mode Exit fullscreen mode

This works, but it’s a lot of code to write for every model, especially in larger applications. Here’s where Equatable comes to the rescue.

Meet Equatable: Simplifying Object Equality

Equatable is a package that makes it easy to compare objects by simply listing the properties that define equality. It saves us from writing the boilerplate == and hashCode logic.

Installing Equatable

To get started, add Equatable to your project by adding it to pubspec.yaml:

dependencies:
  equatable: ^2.0.3
Enter fullscreen mode Exit fullscreen mode

After running flutter pub get, you’re ready to start using Equatable.

Implementing Equatable in Your Class

Now, let’s rewrite the User class using Equatable.

import 'package:equatable/equatable.dart';

class User extends Equatable {
  final int id;
  final String name;

  User({required this.id, required this.name});

  @override
  List<Object?> get props => [id, name];
}
Enter fullscreen mode Exit fullscreen mode

Let’s break down what’s happening here:

  • Inheritance: The User class now extends Equatable, which allows us to implement easy equality checks.
  • props: We override the props getter to list the properties (id and name) that should be considered when comparing instances.

Testing the Equatable Implementation

Now let’s see the result of comparing two instances with the same properties:

void main() {
  var user1 = User(id: 1, name: 'Alice');
  var user2 = User(id: 1, name: 'Alice');

  print(user1 == user2); // true
}
Enter fullscreen mode Exit fullscreen mode

With Equatable, user1 == user2 now returns true, as expected!

Challenge Time: Practice with Equatable

It’s your turn! Let’s go through a hands-on exercise to solidify your understanding of Equatable.

  • Create a Class: Define a Product class with properties like id, name, and price.
  • Use Equatable: Extend Equatable and override props to include your properties.
  • Test It Out: Create two instances with the same property values and test equality between them.

Here’s a template to get you started:

import 'package:equatable/equatable.dart';

class Product extends Equatable {
  final int id;
  final String name;
  final double price;

  Product({required this.id, required this.name, required this.price});

  @override
  List<Object?> get props => [id, name, price];
}

void main() {
  var product1 = Product(id: 101, name: 'Laptop', price: 1500.0);
  var product2 = Product(id: 101, name: 'Laptop', price: 1500.0);

  print(product1 == product2); // true
}
Enter fullscreen mode Exit fullscreen mode

Try adding or changing properties in the Product class and see how Equatable handles it automatically.

Why Equatable Is Essential for Flutter Bloc

When managing state with Bloc, your states and events are frequently compared. Without Equatable, you’d have to override == and hashCode in every state and event class, which could lead to bulky, error-prone code. With Equatable, state and event comparison are straightforward and requires minimal setup. This makes your Bloc codebase cleaner, more efficient, and easier to maintain.

Example: Using Equatable in Bloc States

Let’s look at a small example of how Equatable fits into a Bloc setup. Suppose we have a counter app where our states represent different values of a counter.

import 'package:equatable/equatable.dart';

abstract class CounterState extends Equatable {
  const CounterState();
}

class CounterValue extends CounterState {
  final int value;

  const CounterValue(this.value);

  @override
  List<Object?> get props => [value];
}
Enter fullscreen mode Exit fullscreen mode

Here, CounterValue extends CounterState, which itself extends Equatable. Thanks to Equatable, our Bloc now knows when two states are effectively the same, leading to smoother, optimized performance in state transitions.

Wrapping Up

Today we learned:

  • Why object equality is crucial in state management.
  • How to manually override equality in Dart.
  • How Equatable simplifies equality checks, reducing boilerplate code and making comparisons effortless.
  • How to apply Equatable in state classes within Bloc for optimal performance.

Equatable is a small but powerful tool, making equality in - - Flutter Bloc more straightforward and intuitive. Spend a few minutes practicing what you’ve learned today. Try adding - ------Equatable to different classes in your project, and see the difference in code cleanliness and readability.

Top comments (0)