Comparing objects in Dart made easy with Equatable.

Stephan E.G. Veenstra
Pinch.nl
Published in
4 min readJul 19, 2021

Sometimes we need to compare objects to see if they are equal to one another. This can easily be accomplished with the equatable package.

I took a closer look into this concept of equality when I started working on the puzzle game that I’m building. It was not the first time I’ve had to compare objects, but this time it became more crucial. Now my game heavily relies on comparing objects and by doing it the 'right' way, it even made my code easier to read and understand.

Allow me to enlighten you.

Same vs Equal

So there is a difference between objects being the same, and objects being equal.

Photo by Jakob Rosen on Unsplash

When we are talking about objects being the same, we refer to the same instance of an object. When you’re carpooling to work, you and your colleague are in the same car.

On the other hand, when you and your colleague both go with your own cars, they could be considered equal, depending on which properties you decide to compare. Like when the cars are the same make, year and color, we could consider them equal, right?

Comparing objects in Dart

The most obvious way to compare two objects is of course the == operator. This will in fact check for equality. To check if two objects are the same instance, we should use identical() .

identical can act a little unexpected when working with primitives like int or String . You hardly ever need to use identical but you should be aware.

Let’s checkout some examples:

As you probable noticed, both identical and equality checks result in the same output. So what is going on here?

Hashcode and ==

By default, each instance you create is unique. This explains the behavior in the example above.

When we want two instances to be considered equal, we must override == operator and hashcode on that class. When overriding, it’s important that the outcomes of both overrides are consistent.

When we want two objects to be considered equal the == operator should return true and hashcode on both objects should be returning the same int .

Let’s do this for our car example:

Now that we’ve implemented our own logic for == and hashcode , we decide which instances of Cars are equal. We do this by checking the three properties; color, make and year. We also calculate a new hashcode based on these properties.

As you can see in the example, car1 and car2 are instantiated with the same properties, so they are now considered equal.

car3 is completely different, and is therefor not equal to car1.

How we implement the == and hashcode logic is completely up to us. We could leave out certain properties if we’d like. The important thing is that both == and hashcode behave similar. If == says the objects are equal, then hashcode of both objects should also return the same value.

As you might think after seeing the example above; “damn, that’s a lot of work for something so simple”. And you are right. In the example above it takes up most of the class for a simple class that only holds a little bit of data.

Not only that, it is very error prone. The tiniest mistake can lead to unexpected behavior.

Luckily, there is a solution…

Equatable

To help you gain the advantages of object equality and prevent you from making application breaking mistakes, there is the equatable package.

Equatable will do the necessary overrides for you. All you have to do is tell it which properties to take into account by implementing the props getter.

Let’s update our example so it uses Equatable.

That’s it. This will now result in the same outcome as our tedious manual override.

If your class already extends another class, you can use the EquatableMixin instead.

Using object equality in your code

We can now use our new ‘equatable’ objects to write code that’s easier to read, understand and maintain.

Comparing objects

Let’s start with a simple example of comparing objects that don’t override == and hashcode or implement Equatable.

As you can see, we have to manually compare each and every property of which we decided to be of importance for our equality check. And this example only has three properties…

So let’s now do this for when Car extends Equatable.

Now doesn’t this look way better, and less error prone? I think so too!

Sets and Maps

Not only we can use it to compare objects, Dart uses it as well. Sets and Maps use equality comparison to define uniqueness. Set uses equality checks to prevent two equal objects to be entered and Map uses it for the keys.

Pro tip: If you want all the distinct objects in a List (of items that extend Equatable ) you can simply do: myList.toSet().

Unit tests

Last, but certainly not least, Equatable objects make it so much easier for us to write unit tests. Similar to what we’ve done in ‘Comparing objects’ above, we can simplify our expect.

Conclusion

Equatable makes it much easier for us to create comparable objects by allowing us to tell which fields to take into account.

Comparable objects can improve the readability and maintainability of our code by making comparing shorter and less error prone.

--

--

Stephan E.G. Veenstra
Pinch.nl

When Stephan learned about Flutter back in 2018, he knew this was his Future<>. In 2021 he quit his job and became a full-time Flutter dev.