Last updated:
0 purchases
equatable
Simplify Equality Comparisons
Overview #
Being able to compare objects in Dart often involves having to override the == operator as well as hashCode.
Not only is it verbose and tedious, but failure to do so can lead to inefficient code which does not behave as we expect.
By default, == returns true if two objects are the same instance.
Let's say we have the following class:
class Person {
const Person(this.name);
final String name;
}
copied to clipboard
We can create instances of Person like so:
void main() {
final Person bob = Person("Bob");
}
copied to clipboard
Later if we try to compare two instances of Person either in our production code or in our tests we will run into a problem.
print(bob == Person("Bob")); // false
copied to clipboard
For more information about this, you can check out the official Dart Documentation.
In order to be able to compare two instances of Person we need to change our class to override == and hashCode like so:
class Person {
const Person(this.name);
final String name;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
}
copied to clipboard
Now if we run the following code again:
print(bob == Person("Bob")); // true
copied to clipboard
it will be able to compare different instances of Person.
You can see how this can quickly become a hassle when dealing with complex classes. This is where Equatable comes in!
What does Equatable do? #
Equatable overrides == and hashCode for you so you don't have to waste your time writing lots of boilerplate code.
There are other packages that will actually generate the boilerplate for you; however, you still have to run the code generation step which is not ideal.
With Equatable there is no code generation needed and we can focus more on writing amazing applications and less on mundane tasks.
Usage #
First, we need to do add equatable to the dependencies of the pubspec.yaml
dependencies:
equatable: ^2.0.0
copied to clipboard
Next, we need to install it:
# Dart
pub get
# Flutter
flutter packages get
copied to clipboard
Lastly, we need to extend Equatable
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
}
copied to clipboard
When working with json:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
factory Person.fromJson(Map<String, dynamic> json) {
return Person(json['name']);
}
}
copied to clipboard
We can now compare instances of Person just like before without the pain of having to write all of that boilerplate.
Note: Equatable is designed to only work with immutable objects so all member variables must be final (This is not just a feature of Equatable - overriding a hashCode with a mutable value can break hash-based collections).
Equatable also supports const constructors:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
}
copied to clipboard
toString Implementation #
Equatable can implement toString method including all the given props. If you want that behaviour for a specific Equatable object, just include the following:
@override
bool get stringify => true;
copied to clipboard
For instance:
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
@override
bool get stringify => true;
}
copied to clipboard
For the name Bob, the output will be:
Person(Bob)
This flag by default is false and toString will return just the type:
Person
EquatableConfig
stringify can also be configured globally for all Equatable instances via EquatableConfig
EquatableConfig.stringify = true;
copied to clipboard
If stringify is overridden for a specific Equatable class, then the value of EquatableConfig.stringify is ignored.
In other words, the local configuration always takes precedence over the global configuration.
Note: EquatableConfig.stringify defaults to true in debug mode and false in release mode.
Recap #
Without Equatable #
class Person {
const Person(this.name);
final String name;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Person &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
}
copied to clipboard
With Equatable #
import 'package:equatable/equatable.dart';
class Person extends Equatable {
const Person(this.name);
final String name;
@override
List<Object> get props => [name];
}
copied to clipboard
EquatableMixin #
Sometimes it isn't possible to extend Equatable because your class already has a superclass.
In this case, you can still get the benefits of Equatable by using the EquatableMixin.
Usage #
Let's say we want to make an EquatableDateTime class, we can use EquatableMixin like so:
class EquatableDateTime extends DateTime with EquatableMixin {
EquatableDateTime(
int year, [
int month = 1,
int day = 1,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0,
]) : super(year, month, day, hour, minute, second, millisecond, microsecond);
@override
List<Object> get props {
return [year, month, day, hour, minute, second, millisecond, microsecond];
}
}
copied to clipboard
Now if we want to create a subclass of EquatableDateTime, we can just override props.
class EquatableDateTimeSubclass extends EquatableDateTime {
final int century;
EquatableDateTimeSubclass(
this.century,
int year,[
int month = 1,
int day = 1,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0,
]) : super(year, month, day, hour, minute, second, millisecond, microsecond);
@override
List<Object> get props => super.props..addAll([century]);
}
copied to clipboard
Performance #
You might be wondering what the performance impact will be if you use Equatable.
Results (average over 10 runs) #
Equality Comparison A == A
Class
Runtime (μs)
Manual
0.193
Empty Equatable
0.191
Hydrated Equatable
0.190
Instantiation A()
Class
Runtime (μs)
Manual
0.165
Empty Equatable
0.181
Hydrated Equatable
0.182
*Performance Tests run using: Dart VM version: 2.4.0
Maintainers #
Felix Angelov
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.